Skip to content

e2e flow#467

Open
CodeGhost21 wants to merge 17 commits intotinyhumansai:mainfrom
CodeGhost21:e2e-flow
Open

e2e flow#467
CodeGhost21 wants to merge 17 commits intotinyhumansai:mainfrom
CodeGhost21:e2e-flow

Conversation

@CodeGhost21
Copy link
Copy Markdown
Collaborator

@CodeGhost21 CodeGhost21 commented Apr 9, 2026

  • feat(e2e): overhaul E2E test suite with comprehensive flow coverage
  • feat(e2e): overhaul E2E test suite with comprehensive flow coverage
  • fix(e2e): remove unused imports to pass lint
  • style: apply prettier formatting

Summary by CodeRabbit

  • New Features

    • Text Autocomplete panel with runtime checks; Voice Intelligence skill with STT/TTS and hotkey settings
    • Memory system (store/recall docs & KV); Automation & Scheduling with cron support
    • Rewards & Progression UI; macOS distribution verification
  • Improvements

    • Expanded integrations and chat coverage (Discord, Gmail, Telegram, Notion) and chat interface verification
    • Better Settings navigation, onboarding flow, accessibility labels on skill actions, and skill CTA test IDs
    • Defensive billing UI handling for missing numeric fields
  • Tests

    • Large expansion of end-to-end coverage and added TEST_COVERAGE_MATRIX documentation

Rewrite and expand E2E specs to match actual app capabilities: add new
specs for auth session management, chat interface, chat+skills integrations,
discord, automation scheduling, memory system, permissions, rewards/settings,
system resource access, text autocomplete, and macOS distribution. Remove
obsolete specs (card/crypto payment, skill-execution, skill-multi-round,
conversations-web-channel, auth-access-control) that tested unimplemented
features. Update shared helpers, element helpers, mock API, WDIO config,
and run scripts for the new test matrix.
Rewrite and expand E2E specs to match actual app capabilities: add new
specs for auth session management, chat interface, chat+skills integrations,
discord, automation scheduling, memory system, permissions, rewards/settings,
system resource access, text autocomplete, and macOS distribution. Remove
obsolete specs (card/crypto payment, skill-execution, skill-multi-round,
conversations-web-channel, auth-access-control) that tested unimplemented
features. Update shared helpers, element helpers, mock API, WDIO config,
and run scripts for the new test matrix.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Restructures E2E infrastructure and specs: adds many RPC-driven end-to-end suites and helpers, rewrites cross‑platform runner scripts and CI jobs (macOS/Windows), updates element/navigation utilities for Mac2/tauri-driver, removes several legacy UI/request-log specs, and makes defensive UI and backend cron validation changes.

Changes

Cohort / File(s) Summary
E2E orchestration & scripts
app/package.json, app/scripts/e2e-*.sh, app/scripts/e2e-run-all-flows.sh, app/scripts/e2e-resolve-node-appium.sh, app/scripts/e2e-run-spec.sh, app/scripts/e2e-build.sh, .github/workflows/test.yml
Reworked cross-platform E2E build/run scripts and CI: Windows/macOS support, altered Appium/tauri-driver resolution and invocation, copy mock config into per-user paths, changed run-all flow ordering and added/removed flow labels, added workflow input for Windows E2E.
WDIO / test runner config
app/test/wdio.conf.ts
Platform capability mapping updated (Linux & Windows treated as tauri-driver) and spec glob path corrected.
E2E helpers & platform utilities
app/test/e2e/helpers/*, app/test/e2e/helpers/platform.ts, app/test/e2e/helpers/core-schema.ts
Added core RPC schema fetchers; improved XPath escaping; Mac2 scrolling/click fallbacks; new clickByTestId and scrolling helpers; expanded tauri-driver detection to Windows; onboarding/login/shared-flow rewrites and snackbar dismissal.
E2E specs — added
app/test/e2e/specs/... (auth-session-management.spec.ts, automation-scheduling.spec.ts, chat-interface-flow.spec.ts, chat-skills-integrations.spec.ts, discord-flow.spec.ts, macos-distribution.spec.ts, memory-system.spec.ts, permissions-system-access.spec.ts, system-resource-access.spec.ts, text-autocomplete-flow.spec.ts, voice-mode.spec.ts, rewards-flow.spec.ts, settings-flow.spec.ts)
Added 13 new E2E suites focused on RPC discovery, capability validation, and selective UI flows (macOS-only checks where applicable).
E2E specs — refactored
app/test/e2e/specs/login-flow.spec.ts, gmail-flow.spec.ts, local-model-runtime.spec.ts, notion-flow.spec.ts, screen-intelligence.spec.ts, telegram-flow.spec.ts, voice-mode.spec.ts
Converted many UI/request-log heavy suites to RPC-driven checks, updated onboarding markers and retries, integrated shared helpers and Mac2 fallbacks.
E2E specs — removed
app/test/e2e/specs/auth-access-control.spec.ts, card-payment-flow.spec.ts, crypto-payment-flow.spec.ts, conversations-web-channel-flow.spec.ts, skill-execution-flow.spec.ts, skill-multi-round.spec.ts
Deleted six legacy E2E spec files and their request-log heavy flows.
E2E coverage doc
app/test/e2e/TEST_COVERAGE_MATRIX.md
Added coverage matrix documenting current/added/removed backend flow specs.
E2E run helpers / test runner changes
app/test/e2e/TEST_COVERAGE_MATRIX.md, app/test/wdio.conf.ts
Test coverage documentation added; spec path and driver mapping updated.
UI — skills accessibility & props
app/src/components/skills/shared.tsx, app/src/components/skills/SkillCard.tsx, app/src/pages/Skills.tsx
Added aria-labels to SkillActionButton, introduced optional skillId prop, and pass skillId for built-in skills; CTA testids set when id present.
UI — billing defensive fixes
app/src/components/settings/panels/billing/InferenceBudget.tsx, .../PayAsYouGoCard.tsx
Defensive null/undefined coalescing for budget/credit numeric fields to avoid NaN/divide-by-zero and ensure safe rendering.
Mock API
scripts/mock-api-core.mjs
Adjusted usage/plan payload fields and billing fields, flattened OAuth connect responses to top-level oauthUrl/state, and enhanced chat completions mock to emit initial tool_calls when configured.
Backend — subconscious/cron
src/openhuman/subconscious/types.rs, src/openhuman/subconscious/schemas.rs, src/openhuman/subconscious/store.rs, src/openhuman/subconscious/engine.rs, src/openhuman/subconscious/integration_test.rs
Introduced cron validation/try_cron and TryFrom/FromStr for TaskRecurrence; propagate parse errors for persisted cron values; updated tests and fixtures to use validated cron construction.
Backend tests — JSON-RPC E2E
tests/json_rpc_e2e.rs
Added a Tokio JSON-RPC E2E test covering automation & scheduling: create/update/delete tasks, cron validation, triggers, and run-history assertions.
New E2E helpers module
app/test/e2e/helpers/core-schema.ts
New exports: RpcMethodSchema, fetchCoreSchemaDump, fetchCoreRpcMethods, expectRpcMethod for RPC schema discovery/assertions.

Sequence Diagram(s)

sequenceDiagram
  participant WD as WDIO/Test Runner
  participant MS as Mock Server
  participant APP as App (Renderer / WebView)
  participant CORE as Core JSON‑RPC
  participant APPIUM as Appium / tauri-driver

  WD->>MS: start mock server
  WD->>APP: launch app / trigger deep-link auth
  APP->>CORE: fetch /rpc schema & call openhuman.* RPCs
  CORE-->>APP: RPC responses (ok / error / tool_calls)
  WD->>APPIUM: request macOS UI actions (when Mac2)
  APPIUM-->>APP: perform clicks/scrolls/keyboard
  WD->>MS: assert recorded HTTP requests (when used)
  WD->>MS: stop mock server
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • senamakel
  • graycyrus

"🐰 I hopped through tests with nimble feet,
RPCs and mocks made the flow complete.
From cron to voice and skills that sing,
Appium scrolls and CI bells ring.
Carrots for tests — a celebratory treat!"

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'e2e flow' is vague and generic, using non-descriptive terms that fail to convey meaningful information about the substantial changeset. Replace with a more specific title that reflects the main change, such as 'feat(e2e): comprehensive E2E test suite overhaul with new flow coverage' or 'refactor(e2e): expand test coverage and cross-platform compatibility'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/test/e2e/helpers/element-helpers.ts (1)

28-45: ⚠️ Potential issue | 🟠 Major

Don't XML-escape &, <, and > in XPath string literals.

XPath locators are passed as plain strings to the XPath engine, not serialized XML. Converting A & B to A &amp; B changes the text being matched and will break waitForText() / textExists() for content containing these characters. Only quote-handling needs special treatment—choose the other quote type or use concat() for mixed quotes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/helpers/element-helpers.ts` around lines 28 - 45, The function
xpathStringLiteral currently XML-escapes &, <, and > which alters the text given
to the XPath engine; change it to operate on the raw input string (use the
existing parameter text instead of xmlSafe) and only handle quote collisions: if
text contains no double-quote return `"${text}"`; if no single-quote return
`'${text}'`; otherwise split the original text on double quotes and build a
concat(...) expression that alternates quoted substrings and the XPath literal
for a quote (e.g. "'\"'"), preserving the original characters (&, <, >) exactly;
update any variable names accordingly and keep the function name
xpathStringLiteral.
app/test/e2e/helpers/shared-flows.ts (1)

34-44: ⚠️ Potential issue | 🟠 Major

Keep waitForHomePage() specific to Home markers.

Adding No messages yet and Type a message makes /conversations satisfy this helper, so callers can now pass "reached Home" checks without ever leaving the chat screen. If you need generic logged-in-shell readiness, please add a separate helper instead of weakening this one.

Suggested fix
   const candidates = [
     'Test',
     'Good morning',
     'Good afternoon',
     'Good evening',
     'Message OpenHuman',
     'Upgrade to Premium',
-    'No messages yet',
-    'Type a message',
   ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/helpers/shared-flows.ts` around lines 34 - 44, The
waitForHomePage helper (waitForHomePage) currently includes generic chat/shell
markers ('No messages yet' and 'Type a message') that allow the /conversations
screen to satisfy Home checks; remove those two entries from the candidates
array so the function only looks for Home-specific markers (e.g., 'Test', 'Good
morning', 'Good afternoon', 'Good evening', 'Message OpenHuman', 'Upgrade to
Premium'), and if you need a broader "logged-in shell ready" check create a new
helper (e.g., waitForLoggedInShell) that includes the generic chat markers
instead of weakening waitForHomePage.
🟡 Minor comments (6)
app/test/e2e/specs/telegram-flow.spec.ts-100-106 (1)

100-106: ⚠️ Potential issue | 🟡 Minor

Same tautological assertion pattern.

expect(res.ok || Boolean(res.error)).toBe(true) always passes. This pattern appears multiple times in this file (lines 105, 121, 135, 158, 178, 195, 200).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/telegram-flow.spec.ts` around lines 100 - 106, The test
uses a tautological assertion expect(res.ok || Boolean(res.error)).toBe(true)
which always passes; replace it with an explicit, meaningful check on the RPC
response returned by callOpenhumanRpc('openhuman.channels_connect', ...) —
either assert success with expect(res.ok).toBe(true) when a successful
connection is expected, or assert the expected error with
expect(res.error).toBeTruthy()/toEqual(<expectedError>) when an error is
expected; update all similar occurrences in this spec that assert on the
variable res so each case validates the intended outcome rather than the current
always-true expression.
app/test/e2e/specs/rewards-settings.spec.ts-109-117 (1)

109-117: ⚠️ Potential issue | 🟡 Minor

Tautological assertion — always passes regardless of RPC outcome.

expect(updated.ok || Boolean(updated.error)).toBe(true) will always be true: if ok is true, the expression is true; if ok is false, there's typically an error string making the expression true. This doesn't validate the intended behavior.

🐛 Proposed fix

If the intent is to accept either success or a structured error (not a crash):

-    expect(updated.ok || Boolean(updated.error)).toBe(true);
+    // Accept success, or a well-formed error response (not a network/timeout failure)
+    expect(updated.ok === true || typeof updated.error === 'string').toBe(true);

Or if only success is valid:

-    expect(updated.ok || Boolean(updated.error)).toBe(true);
+    expect(updated.ok).toBe(true);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/rewards-settings.spec.ts` around lines 109 - 117, The
test's tautological assertion must be replaced with a concrete check: update the
assertion in the "11.5.3 — Full State Reset" test that calls
callOpenhumanRpc('openhuman.app_state_update_local_state', ...) to either assert
success explicitly (expect(updated.ok).toBe(true)) or, if you intend to accept a
structured error, assert the exact error shape (e.g.,
expect(updated.error).toBeDefined() and expect(typeof
updated.error).toBe('string') or check for a specific code). Modify the
assertion on the `updated` result returned by callOpenhumanRpc so it verifies
the intended outcome (use `updated.ok` for success-only or detailed checks on
`updated.error` for accepted error responses).
app/test/e2e/specs/chat-skills-integrations.spec.ts-85-88 (1)

85-88: ⚠️ Potential issue | 🟡 Minor

Tautological assertion — same issue as in rewards-settings.spec.ts.

expect(sync.ok || Boolean(sync.error)).toBe(true) will always pass. If the intent is to verify the endpoint responds (either successfully or with a well-formed error), consider a more precise assertion.

🐛 Proposed fix
-    expect(sync.ok || Boolean(sync.error)).toBe(true);
+    // Accept either success or a structured error (endpoint responded)
+    expect(sync.ok === true || typeof sync.error === 'string').toBe(true);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/chat-skills-integrations.spec.ts` around lines 85 - 88,
The test uses a tautological assertion that always passes; replace
expect(sync.ok || Boolean(sync.error)).toBe(true) with a precise check that the
RPC returned a well-formed response: callOpenhumanRpc('openhuman.skills_sync', {
id: 'missing-runtime' }) should yield an object and you should assert either
sync.ok === true OR that sync.error exists and is the correct shape (e.g.,
typeof sync.error === 'string' or has expected properties). Update the assertion
in the test case (referencing callOpenhumanRpc and variable sync) to explicitly
check the presence and type of sync.ok or sync.error using Jest matchers (for
example expect(sync).toBeInstanceOf(Object) and expect(sync.ok === true ||
(sync.error && typeof sync.error === 'string')).toBe(true)).
app/test/e2e/specs/auth-session-management.spec.ts-178-192 (1)

178-192: ⚠️ Potential issue | 🟡 Minor

Token rotation assertion does not verify actual rotation.

The assertion expect(value2.length > 0 || value1.length > 0) only checks that at least one token exists, not that rotation actually occurred. To verify rotation, assert that value2 !== value1 when both are non-empty.

Proposed fix
     const value1 = extractToken(token1);
 
     setMockBehavior('jwt', 'rot2');
     await triggerAuthDeepLink('e2e-rot-token-2');
     await browser.pause(2_000);
     const token2 = await expectRpcOk('openhuman.auth_get_session_token', {});
     const value2 = extractToken(token2);
 
-    expect(value2.length > 0 || value1.length > 0).toBe(true);
+    // At least one token should exist
+    expect(value1.length > 0 || value2.length > 0).toBe(true);
+    // If both tokens exist, they should differ (rotation occurred)
+    if (value1.length > 0 && value2.length > 0) {
+      expect(value2).not.toBe(value1);
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/auth-session-management.spec.ts` around lines 178 - 192,
The test's final assertion doesn't verify rotation; update the assert logic in
the "1.3.2 — Refresh Token Rotation" test (using setMockBehavior,
triggerAuthDeepLink, expectRpcOk, extractToken and the value1/value2 variables)
so that if both value1 and value2 are non-empty you assert value2 !== value1 to
confirm rotation, otherwise fall back to asserting at least one token exists
(preserve the current presence check).
app/test/e2e/specs/auth-session-management.spec.ts-86-92 (1)

86-92: ⚠️ Potential issue | 🟡 Minor

Darwin-specific fallback may mask real failures.

The || process.platform === 'darwin' fallback allows the test to pass on macOS even when the consume call is missing. If the consume call is expected to be absent on macOS, document why. Otherwise, this could hide a bug where deep links don't trigger the expected backend call.

Suggested clarification
     const consumeCall = getRequestLog().find(
       item => item.method === 'POST' && item.url.includes('/telegram/login-tokens/')
     );
     if (!consumeCall) {
       console.log('[AuthSpec] consume call missing:', JSON.stringify(getRequestLog(), null, 2));
     }
-    expect(Boolean(consumeCall) || process.platform === 'darwin').toBe(true);
+    // On macOS with Mac2 driver, the deep link may be handled differently and
+    // the token consume call might not appear in the request log.
+    if (process.platform !== 'darwin') {
+      expect(consumeCall).toBeDefined();
+    } else {
+      console.log('[AuthSpec] Skipping consume call assertion on macOS (Mac2 driver limitation)');
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/auth-session-management.spec.ts` around lines 86 - 92, The
test currently uses a macOS fallback in the assertion
(expect(Boolean(consumeCall) || process.platform === 'darwin').toBe(true)) which
can mask real failures; update the test around consumeCall and getRequestLog()
to either remove the platform shortcut or make the platform behavior explicit:
if the consume call is truly expected to be absent on macOS, add a clear comment
and branch the assertion (if process.platform === 'darwin' then assert the
documented alternative behavior or skip the assertion), otherwise remove the "||
process.platform === 'darwin'" and assert
expect(Boolean(consumeCall)).toBe(true) so failures surface; reference the
consumeCall variable, getRequestLog() call, and the expect(...) expression when
making the change.
app/scripts/e2e-run-spec.sh-108-117 (1)

108-117: ⚠️ Potential issue | 🟡 Minor

User-scoped config directories are not cleaned up on exit.

The script creates ~/.openhuman/users/user-123/config.toml and ~/.openhuman/users/e2e-user/config.toml but the cleanup() function (lines 42-59) only handles the root $E2E_CONFIG_FILE. These user-scoped configs will persist after the test run, potentially affecting subsequent runs or manual testing.

Proposed fix to clean up user-scoped configs
+E2E_USER_CONFIG_DIRS=()
+
 for MOCK_USER_ID in "user-123" "e2e-user"; do
   USER_CONFIG_DIR="$E2E_CONFIG_DIR/users/$MOCK_USER_ID"
   mkdir -p "$USER_CONFIG_DIR"
   cp "$E2E_CONFIG_FILE" "$USER_CONFIG_DIR/config.toml"
+  E2E_USER_CONFIG_DIRS+=("$USER_CONFIG_DIR/config.toml")
 done
 echo "Wrote user-scoped config.toml for mock users"

Then in the cleanup() function, add:

   elif [ -n "${E2E_CONFIG_FILE:-}" ] && [ -f "${E2E_CONFIG_FILE:-}" ]; then
     rm -f "$E2E_CONFIG_FILE"
     echo "Removed E2E config.toml"
   fi
+  # Remove user-scoped E2E configs
+  for ucfg in "${E2E_USER_CONFIG_DIRS[@]}"; do
+    [ -f "$ucfg" ] && rm -f "$ucfg"
+  done
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/scripts/e2e-run-spec.sh` around lines 108 - 117, The cleanup() function
currently only removes E2E_CONFIG_FILE but not the per-user files created in the
loop that sets MOCK_USER_ID and USER_CONFIG_DIR; update cleanup() to remove the
generated user-scoped config files and directories under "$E2E_CONFIG_DIR/users"
(e.g., remove "$E2E_CONFIG_DIR/users/user-123/config.toml" and
"$E2E_CONFIG_DIR/users/e2e-user/config.toml" and prune empty directories) so the
artifacts created by the loop that copies E2E_CONFIG_FILE into USER_CONFIG_DIR
are cleaned on exit; reference the existing symbols cleanup(), E2E_CONFIG_DIR,
E2E_CONFIG_FILE, MOCK_USER_ID, and USER_CONFIG_DIR when making the change.
🧹 Nitpick comments (16)
app/test/e2e/specs/local-model-runtime.spec.ts (2)

23-30: Prefer arrow functions per coding guidelines.

The helpers use function declarations. Per coding guidelines for **/*.{js,jsx,ts,tsx}: "Prefer arrow functions over function declarations."

♻️ Convert to arrow functions
-function stepLog(message: string, context?: unknown): void {
+const stepLog = (message: string, context?: unknown): void => {
   const stamp = new Date().toISOString();
   // ...
-}
+};

-function looksLikeSetupError(error?: string): boolean {
+const looksLikeSetupError = (error?: string): boolean => {
   // ...
-}
+};

Also applies to: 32-40, 42-55, 57-66

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/local-model-runtime.spec.ts` around lines 23 - 30, The
helper functions (e.g., stepLog) are declared with function declarations but
should follow the repo style and be converted to arrow functions: replace
function stepLog(message: string, context?: unknown): void { ... } with a const
stepLog = (message: string, context?: unknown): void => { ... } and do the same
conversion for the other helpers referenced (lines 32-40, 42-55, 57-66) so all
helper functions become const <name> = (...) => ... while preserving parameter
types, return types, and existing implementation and exports.

68-72: requireMethod returns boolean but tests silently pass on missing methods.

When requireMethod returns false, the test returns early without any assertion, making it appear to pass. This could mask missing functionality. Consider using it.skip or at least logging a warning.

♻️ Proposed improvement

Option 1 — Use this.skip() (Mocha) to mark as skipped:

-function requireMethod(methods: Set<string>, method: string, caseId: string): boolean {
-  if (methods.has(method)) return true;
-  console.log(`${LOG_PREFIX} ${caseId}: INCONCLUSIVE — missing RPC method ${method}`);
-  return false;
-}
+function requireMethod(
+  methods: Set<string>,
+  method: string,
+  caseId: string,
+  testContext?: Mocha.Context
+): boolean {
+  if (methods.has(method)) return true;
+  console.warn(`${LOG_PREFIX} ${caseId}: SKIPPED — missing RPC method ${method}`);
+  testContext?.skip();
+  return false;
+}

Then in tests:

it('3.1.1 Model Detection', async function() {
  if (!requireMethod(methods, 'openhuman.local_ai_status', '3.1.1', this)) return;
  // ...
});

Option 2 — At minimum, add an expectation that the test was inconclusive:

   if (!requireMethod(methods, 'openhuman.local_ai_status', '3.1.1')) return;
+  // Test is inconclusive but marked as passed
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/local-model-runtime.spec.ts` around lines 68 - 72, The
current requireMethod function silently returns false which lets tests exit
without failing or marking skipped; update requireMethod(methods: Set<string>,
method: string, caseId: string, ctx?: Mocha.Context) to call ctx?.skip() when
the method is missing (or alternatively throw/assert.fail with a clear message)
and update all test callers (e.g., the test using requireMethod in
local-model-runtime.spec.ts inside the "3.1.1 Model Detection" it block) to pass
the Mocha this context (or handle the thrown assertion) so missing RPC methods
are reported as skipped or failed rather than silently passing.
app/test/e2e/specs/memory-system.spec.ts (2)

9-9: Inter-test dependency via shared mutable state.

bulkDocId0 is set in test 5.2.3 (line 135) and read in test 5.3.1 (lines 143-149). If tests run out of order or 5.2.3 fails, 5.3.1 may behave unexpectedly. Per coding guidelines, each E2E spec should be runnable in isolation.

♻️ Proposed fix — make 5.3.1 self-contained

Test 5.3.1 already has fallback logic to extract a document ID if bulkDocId0 is null. Consider either:

  1. Always extracting from the list (removing the shared state):
-let bulkDocId0: string | null = null;
   it('5.3.1 — Forget Memory Entry: doc_delete removes targeted document', async () => {
-    if (!bulkDocId0) {
-      const listed = await expectRpcOk('openhuman.memory_doc_list', { namespace: NS });
-      const text = JSON.stringify(listed || {});
-      const match = text.match(/"document_id"\s*:\s*"([^"]+)"/);
-      bulkDocId0 = match?.[1] || null;
-    }
+    // Insert a doc specifically for this test
+    const testKey = `${DOC_KEY}-delete-test`;
+    const put = await expectRpcOk('openhuman.memory_doc_put', {
+      namespace: NS,
+      key: testKey,
+      title: 'Delete test',
+      content: 'to be deleted',
+    });
+    const docId = extractDocumentId(put);
+    expect(Boolean(docId)).toBe(true);
  1. Or document the dependency clearly with a comment.

Also applies to: 126-140, 142-158

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/memory-system.spec.ts` at line 9, The tests share mutable
state via the top-level variable bulkDocId0 which is set in test "5.2.3" and
later read in "5.3.1", creating an inter-test dependency; make 5.3.1
self-contained by removing reliance on bulkDocId0: update the test logic in the
"5.3.1" spec to always extract the document ID from the response list (use the
same extraction/fallback code already present) and delete the top-level let
bulkDocId0 declaration (or limit it to the test that needs it) so each spec can
run independently; reference variable bulkDocId0 and the "5.3.1" test to locate
and change the code.

51-51: Brittle string-based assertions.

Assertions like JSON.stringify(put || {}).length > 0 and .includes('42') are fragile and may pass/fail unexpectedly. Consider asserting on the actual structure.

♻️ Example improvement for line 64
-    expect(JSON.stringify(read || {}).includes('42')).toBe(true);
+    // Assert on actual payload structure
+    const value = (read as { value?: { score?: number } })?.value;
+    expect(value?.score).toBe(42);

Also applies to: 55-55, 64-64

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/memory-system.spec.ts` at line 51, The test uses brittle
string-based assertions like JSON.stringify(put || {}).length > 0 and
.includes('42'); replace these with structural assertions against the actual
objects: for the variable put assert it is defined and has expected keys/values
(e.g. expect(put).toBeDefined();
expect(Object.keys(put).length).toBeGreaterThan(0); or
expect(put.someField).toEqual(42) if you expect a numeric value), and for any
contains checks use array/object membership assertions (e.g.
expect(array).toContain(42) or expect(obj).toHaveProperty('key')). Update the
assertions around the put/get variables in the failing tests so they check
object shape and specific fields instead of JSON.stringify or string includes.
app/test/e2e/specs/telegram-flow.spec.ts (2)

68-77: Unused helper function _waitForRequest.

This helper is defined but not called anywhere in the spec. If it's intended for future use, consider adding a TODO comment; otherwise, remove dead code.

🗑️ Remove unused code or add TODO
-async function _waitForRequest(method: string, urlFragment: string, timeout = 15_000) {
-  const deadline = Date.now() + timeout;
-  while (Date.now() < deadline) {
-    const log = getRequestLog();
-    const match = log.find(r => r.method === method && r.url.includes(urlFragment));
-    if (match) return match;
-    await browser.pause(500);
-  }
-  return undefined;
-}

Or if keeping for future use:

+// TODO: Re-enable when mock server request verification is needed
 async function _waitForRequest(method: string, urlFragment: string, timeout = 15_000) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/telegram-flow.spec.ts` around lines 68 - 77, The helper
function _waitForRequest is defined but never used; either remove this dead
function from the spec to keep the test file clean, or if you intend to keep it
for future tests add a brief TODO comment above _waitForRequest explaining its
planned use (e.g., "TODO: used by future webhook request assertions") so
reviewers know it is intentionally retained; locate the function by name
(_waitForRequest) in the telegram-flow.spec.ts and apply one of these two
changes.

329-334: Empty catch block silently swallows errors.

The try/catch silently ignores errors when clicking. While the fallback is reasonable, consider logging when falling back.

♻️ Add diagnostic logging
     try {
       await clickText('Telegram', 10_000);
     } catch {
-      // Fallback: try "Configure"
+      stepLog('clickText("Telegram") failed, falling back to "Configure"');
       await clickText('Configure', 10_000);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/telegram-flow.spec.ts` around lines 329 - 334, The catch
currently swallows any error from clickText('Telegram', 10_000) with no
diagnostics; modify the try/catch around clickText in the telegram-flow.spec.ts
test so the catch captures the error (e.g., catch (err)) and logs a brief
diagnostic before executing the fallback clickText('Configure', 10_000) —
reference the clickText calls and ensure the log includes the error and context
(e.g., which selector/text failed) so failures are visible in test output.
app/test/e2e/specs/rewards-settings.spec.ts (2)

6-13: Consider extracting expectRpcOk to a shared helper.

This helper is duplicated across multiple spec files (chat-skills-integrations.spec.ts, memory-system.spec.ts, rewards-settings.spec.ts). Per coding guidelines, use existing test helpers from app/src/test/ before adding new harness code.

♻️ Suggested location for shared helper

Extract to app/test/e2e/helpers/core-rpc.ts or a new rpc-test-utils.ts:

// In a shared helper file
export async function expectRpcOk(
  method: string,
  params: Record<string, unknown> = {},
  logPrefix = '[RpcSpec]'
) {
  const result = await callOpenhumanRpc(method, params);
  if (!result.ok) {
    console.log(`${logPrefix} ${method} failed`, result.error);
  }
  expect(result.ok).toBe(true);
  return result.result;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/rewards-settings.spec.ts` around lines 6 - 13, Extract the
duplicated expectRpcOk helper into a shared test helper (e.g.,
app/test/e2e/helpers/core-rpc.ts or rpc-test-utils.ts) by moving the async
function expectRpcOk(method: string, params: Record<string, unknown> = {},
logPrefix = '[RpcSpec]') into that file and exporting it; update the specs that
currently define their own copies (rewards-settings.spec.ts,
chat-skills-integrations.spec.ts, memory-system.spec.ts) to import expectRpcOk
from the new helper and remove their local definitions, ensuring the
implementation retains the console logging of the error and the
expect(result.ok).toBe(true) behavior.

99-102: Calling auth_clear_session may affect test isolation.

This test calls auth_clear_session which clears the authentication session. If this spec runs as part of a broader suite, it could invalidate sessions needed by subsequent tests. Per coding guidelines, ensure each E2E spec is runnable in isolation.

Consider adding a comment clarifying this is intentional, or re-authenticating in an after hook if needed for suite isolation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/rewards-settings.spec.ts` around lines 99 - 102, The test
"11.5.1 — Clear App Data: auth_clear_session can be called" calls
openhuman.auth_clear_session which can break test isolation; update the spec so
it either documents that clearing the session is intentional (add a clarifying
comment above the it block referencing auth_clear_session) or restore
authentication for the suite by re-authenticating in an after/afterEach hook
(use the same helpers used elsewhere, e.g.,
expectRpcOk('openhuman.auth_clear_session', {}) and the project’s login helper)
so subsequent tests remain valid; ensure you reference the test case name and
the RPC helpers expectRpcMethod and expectRpcOk when making the change.
app/scripts/e2e-auth.sh (1)

2-4: Comment doesn't match new spec name.

The comment says "auth & access control tests" but the script now runs auth-session-management.spec.ts. Consider updating for clarity.

📝 Proposed fix
 #!/usr/bin/env bash
-# Run E2E auth & access control tests only. See app/scripts/e2e-run-spec.sh.
+# Run E2E auth session management tests only. See app/scripts/e2e-run-spec.sh.
 set -euo pipefail
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/scripts/e2e-auth.sh` around lines 2 - 4, Update the header comment in the
e2e-auth.sh script so it accurately describes the test being run: replace or
reword "Run E2E auth & access control tests" to reflect that the script executes
the auth-session-management.spec.ts E2E test (e.g., "Run E2E auth session
management test"). Edit the top comment in e2e-auth.sh that precedes set -euo
pipefail so it matches the actual spec file referenced by the exec call.
app/test/e2e/specs/chat-skills-integrations.spec.ts (1)

115-126: Duplicate test: 9.3.3 repeats 9.3.1.

Both tests check for the existence of openhuman.voice_status. Test 9.3.3 ("Voice Command Execution") has the same assertion as 9.3.1 ("Voice Input Capture") but with a different description.

♻️ Proposed fix — differentiate or remove

Either remove the duplicate or test a different endpoint for voice command execution:

   it('9.3.3 — Voice Command Execution: voice command surface exists in schema', async () => {
-    expectRpcMethod(methods, 'openhuman.voice_status');
+    // If there's a dedicated voice command endpoint, test it here
+    // Otherwise, this test duplicates 9.3.1 and can be removed
+    expectRpcMethod(methods, 'openhuman.voice_execute_command'); // or appropriate method
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/chat-skills-integrations.spec.ts` around lines 115 - 126,
Test "9.3.3 — Voice Command Execution" is a duplicate of 9.3.1 (both assert
existence of openhuman.voice_status); update the test body for the "Voice
Command Execution" spec so it actually checks the voice-command surface (for
example call expectRpcMethod(methods, 'openhuman.voice_command') or, if the
correct RPC has a different name, assert that name instead or remove the test
entirely if no distinct endpoint should exist); keep references to
expectRpcMethod, methods and the test title so reviewers can locate and verify
the change.
app/test/e2e/specs/auth-session-management.spec.ts (2)

51-55: Regex-based token extraction is fragile.

Parsing JSON with regex can break on escaped characters or nested structures. Consider using JSON.parse with proper error handling.

Proposed safer implementation
 function extractToken(result: unknown): string {
-  const payload = JSON.stringify(result || {});
-  const match = payload.match(/"token"\s*:\s*"([^"]+)"/);
-  return match?.[1] || '';
+  try {
+    const obj = typeof result === 'object' ? result : {};
+    // Handle nested result wrapper
+    const data = (obj as Record<string, unknown>)?.result ?? obj;
+    return String((data as Record<string, unknown>)?.token ?? '');
+  } catch {
+    return '';
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/auth-session-management.spec.ts` around lines 51 - 55, The
extractToken function currently uses a regex on JSON string output which is
brittle; update extractToken to safely parse the input: if result is a string
attempt JSON.parse inside a try/catch, if result is an object access
result.token with a proper type guard, and on any parse/lookup failure return an
empty string (log or swallow error as appropriate). Ensure the implementation
handles null/undefined, non-string inputs, and escaped characters by using
JSON.parse and explicit checks rather than regex.

1-1: Consider removing @ts-nocheck and fixing type issues.

Disabling TypeScript checking hides potential bugs. This pattern appears across all new spec files. Consider addressing the underlying type issues (likely around browser global, expect matchers, or RPC result typing) once and removing these directives.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/auth-session-management.spec.ts` at line 1, Remove the
top-level "@ts-nocheck" and fix the underlying TypeScript errors by properly
typing test globals and RPC results: add/import the correct test
framework/global types (e.g., declare or import Playwright/Cypress types so
"browser" is recognized), import or augment Jest/expect matchers if custom
matchers are used, and type RPC return values used in the spec (ensure functions
like the RPC call and its result have explicit interfaces). Update the spec
file's imports or add a local triple-slash reference/ambient declaration for the
testing globals and explicitly type variables like the RPC response, browser,
and expect usage so the file passes TS checks without the nocheck.
app/test/e2e/specs/discord-flow.spec.ts (1)

270-275: Consider using static import for scrollToFindText.

The dynamic import works but is inconsistent with other helper imports. Since scrollToFindText is from element-helpers.ts which is already imported, add it to the static import at the top.

Proposed fix
 import {
   clickText,
   dumpAccessibilityTree,
+  scrollToFindText,
   textExists,
   waitForWebView,
   waitForWindowVisible,
 } from '../helpers/element-helpers';

Then at line 272-274:

     } catch {
       // Fallback: scroll to find it
-      const { scrollToFindText } = await import('../helpers/element-helpers');
       await scrollToFindText('Discord');
       await clickText('Discord', 10_000);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/discord-flow.spec.ts` around lines 270 - 275, Add
scrollToFindText to the existing static import from element-helpers (so you no
longer do a dynamic import inside the catch block) and then remove the dynamic
import in the catch block; simply call scrollToFindText('Discord') followed by
clickText('Discord', 10_000). Update the top-of-file import list to include
scrollToFindText and ensure any local references still match the function name.
app/scripts/e2e-run-all-flows.sh (1)

33-35: Consider documenting why specs are commented out.

These commented-out specs (skills-registry, rewards-settings, navigation) reduce test coverage. Adding brief comments explaining the reason (e.g., "# Disabled: pending mock backend updates" or "# TODO: re-enable after feature stabilizes") would help future maintainers understand the intent.

Example documentation
-# run "test/e2e/specs/skills-registry.spec.ts" "skills-registry"
-# run "test/e2e/specs/rewards-settings.spec.ts" "rewards-settings"
-# run "test/e2e/specs/navigation.spec.ts" "navigation"
+# Temporarily disabled — skills-registry needs mock tool_calls support
+# run "test/e2e/specs/skills-registry.spec.ts" "skills-registry"
+# Temporarily disabled — rewards UI redesign in progress
+# run "test/e2e/specs/rewards-settings.spec.ts" "rewards-settings"
+# run "test/e2e/specs/navigation.spec.ts" "navigation"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/scripts/e2e-run-all-flows.sh` around lines 33 - 35, The three
commented-out test invocations for "skills-registry", "rewards-settings", and
"navigation" in e2e-run-all-flows.sh currently lack context; update the script
by replacing each bare comment with a short explanatory comment (e.g., "#
Disabled: <reason> (TODO/issue `#1234`)" or "# Temporarily skipped until
<service/mock/feature> is fixed — re-enable after <condition>") next to the
corresponding lines so maintainers know why run
"test/e2e/specs/skills-registry.spec.ts" "skills-registry", run
"test/e2e/specs/rewards-settings.spec.ts" "rewards-settings", and run
"test/e2e/specs/navigation.spec.ts" "navigation" are disabled and what should
trigger re-enablement.
app/test/e2e/specs/permissions-system-access.spec.ts (1)

103-111: Early return causes silent pass rather than skip.

When requireMethod returns false, the test exits early and passes silently. Consider using this.skip() (Mocha) to properly mark the test as skipped, which provides better visibility in test reports.

Proposed fix
-function requireMethod(methods: Set<string>, method: string, caseId: string): boolean {
+function requireMethod(
+  methods: Set<string>,
+  method: string,
+  caseId: string,
+  testContext?: Mocha.Context
+): boolean {
   if (methods.has(method)) return true;
   if (STRICT) {
     expect(methods.has(method)).toBe(true);
     return false;
   }
   console.log(`${LOG_PREFIX} ${caseId}: INCONCLUSIVE — RPC method ${method} not registered`);
+  testContext?.skip();
   return false;
 }

Then in tests:

-    it('2.1.1 — Accessibility Permission: status exposes accessibility field', async () => {
-      if (!requireMethod(methods, 'openhuman.screen_intelligence_status', '2.1.1')) return;
+    it('2.1.1 — Accessibility Permission: status exposes accessibility field', async function () {
+      if (!requireMethod(methods, 'openhuman.screen_intelligence_status', '2.1.1', this)) return;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/permissions-system-access.spec.ts` around lines 103 - 111,
The helper requireMethod currently returns false on missing RPC methods which
leads tests to exit early and pass silently; instead make it use Mocha's skip
mechanism by changing requireMethod to accept a Mocha test context (or be called
with .call(this)) and, when STRICT is false, call ctx.skip() (or this.skip())
with a descriptive message using LOG_PREFIX and caseId; when STRICT is true keep
the expect assertion but do not silently return false — throw or let the
assertion fail. Update every caller of requireMethod to pass the test context
(or call it with .call(this, methods, method, caseId)) so skips are properly
recorded.
app/test/e2e/specs/screen-intelligence.spec.ts (1)

47-56: waitForAnyText is duplicated across spec files.

This helper appears in multiple specs (screen-intelligence.spec.ts, voice-mode.spec.ts). Consider extracting it to element-helpers.ts for reuse and consistency. As per coding guidelines: "Use existing test helpers from app/src/test/... before adding new harness code".

Move to element-helpers.ts

In app/test/e2e/helpers/element-helpers.ts:

export async function waitForAnyText(
  candidates: string[],
  timeout = 15_000
): Promise<string | null> {
  const deadline = Date.now() + timeout;
  while (Date.now() < deadline) {
    for (const t of candidates) {
      if (await textExists(t)) return t;
    }
    await browser.pause(500);
  }
  return null;
}

Then import in specs instead of duplicating.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/screen-intelligence.spec.ts` around lines 47 - 56, The
function waitForAnyText is duplicated; extract it into a single exported helper
(element-helpers.ts) as async function waitForAnyText(candidates: string[],
timeout = 15000): Promise<string | null>, keep the same logic (uses textExists
and browser.pause), export it, then remove the duplicate implementations in the
specs and import the helper where needed (e.g., replace the local definitions in
screen-intelligence.spec.ts and voice-mode.spec.ts with an import of
waitForAnyText). Ensure the helper is exported with the exact name and update
the spec imports so TypeScript/ESLint resolves it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 91d029be-b52a-4a5e-bfc9-08c927ee2345

📥 Commits

Reviewing files that changed from the base of the PR and between 382038f and f084773.

⛔ Files ignored due to path filters (1)
  • app/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (40)
  • app/package.json
  • app/scripts/e2e-auth.sh
  • app/scripts/e2e-crypto-payment.sh
  • app/scripts/e2e-payment.sh
  • app/scripts/e2e-resolve-node-appium.sh
  • app/scripts/e2e-run-all-flows.sh
  • app/scripts/e2e-run-spec.sh
  • app/src/components/skills/shared.tsx
  • app/test/e2e/TEST_COVERAGE_MATRIX.md
  • app/test/e2e/helpers/core-schema.ts
  • app/test/e2e/helpers/element-helpers.ts
  • app/test/e2e/helpers/shared-flows.ts
  • app/test/e2e/specs/auth-access-control.spec.ts
  • app/test/e2e/specs/auth-session-management.spec.ts
  • app/test/e2e/specs/automation-scheduling.spec.ts
  • app/test/e2e/specs/card-payment-flow.spec.ts
  • app/test/e2e/specs/chat-interface-flow.spec.ts
  • app/test/e2e/specs/chat-skills-integrations.spec.ts
  • app/test/e2e/specs/conversations-web-channel-flow.spec.ts
  • app/test/e2e/specs/crypto-payment-flow.spec.ts
  • app/test/e2e/specs/discord-flow.spec.ts
  • app/test/e2e/specs/gmail-flow.spec.ts
  • app/test/e2e/specs/local-model-runtime.spec.ts
  • app/test/e2e/specs/login-flow.spec.ts
  • app/test/e2e/specs/macos-distribution.spec.ts
  • app/test/e2e/specs/memory-system.spec.ts
  • app/test/e2e/specs/notion-flow.spec.ts
  • app/test/e2e/specs/permissions-system-access.spec.ts
  • app/test/e2e/specs/rewards-settings.spec.ts
  • app/test/e2e/specs/screen-intelligence.spec.ts
  • app/test/e2e/specs/skill-execution-flow.spec.ts
  • app/test/e2e/specs/skill-multi-round.spec.ts
  • app/test/e2e/specs/system-resource-access.spec.ts
  • app/test/e2e/specs/telegram-flow.spec.ts
  • app/test/e2e/specs/text-autocomplete-flow.spec.ts
  • app/test/e2e/specs/voice-mode.spec.ts
  • app/test/wdio.conf.ts
  • scripts/mock-api-core.mjs
  • src/openhuman/subconscious/schemas.rs
  • tests/json_rpc_e2e.rs
💤 Files with no reviewable changes (8)
  • app/scripts/e2e-payment.sh
  • app/scripts/e2e-crypto-payment.sh
  • app/test/e2e/specs/skill-multi-round.spec.ts
  • app/test/e2e/specs/crypto-payment-flow.spec.ts
  • app/test/e2e/specs/skill-execution-flow.spec.ts
  • app/test/e2e/specs/conversations-web-channel-flow.spec.ts
  • app/test/e2e/specs/auth-access-control.spec.ts
  • app/test/e2e/specs/card-payment-flow.spec.ts

senamakel
senamakel previously approved these changes Apr 9, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/openhuman/subconscious/schemas.rs (1)

330-342: ⚠️ Potential issue | 🟠 Major

Reject unknown recurrence strings instead of silently coercing to Pending.

At Line 339, Some(_) => Some(TaskRecurrence::Pending) accepts typos/invalid values and mutates task behavior unexpectedly. Treat only "pending" as pending, and return an error for other strings.

Proposed fix
             recurrence: {
-                let raw = params.get("recurrence").and_then(|v| v.as_str());
-                match raw {
+                let raw = params.get("recurrence").and_then(|v| v.as_str());
+                match raw {
                     Some("once") => Some(TaskRecurrence::Once),
+                    Some("pending") => Some(TaskRecurrence::Pending),
                     Some(s) if s.starts_with("cron:") => {
                         let expr = &s["cron:".len()..];
                         validate_cron_expression(expr)?;
                         Some(TaskRecurrence::Cron(expr.to_string()))
                     }
-                    Some(_) => Some(TaskRecurrence::Pending),
+                    Some(other) => {
+                        return Err(format!(
+                            "invalid recurrence '{other}', expected 'once' | 'pending' | 'cron:<expr>'"
+                        ))
+                    }
                     None => None,
                 }
             },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/subconscious/schemas.rs` around lines 330 - 342, The recurrence
match currently coerces any unknown string into TaskRecurrence::Pending; change
the match in the recurrence block (the params.get("recurrence") match) to only
accept "once", "cron:..." and "pending" explicitly (map "pending" to
TaskRecurrence::Pending), and for any other Some(s) return an error instead of
returning Pending; keep using validate_cron_expression(...) for cron entries and
propagate/construct the function's existing error type with a clear message
including the invalid string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/openhuman/subconscious/schemas.rs`:
- Around line 330-342: The recurrence match currently coerces any unknown string
into TaskRecurrence::Pending; change the match in the recurrence block (the
params.get("recurrence") match) to only accept "once", "cron:..." and "pending"
explicitly (map "pending" to TaskRecurrence::Pending), and for any other Some(s)
return an error instead of returning Pending; keep using
validate_cron_expression(...) for cron entries and propagate/construct the
function's existing error type with a clear message including the invalid
string.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d64302be-ba42-46aa-b6cb-53e4a5328052

📥 Commits

Reviewing files that changed from the base of the PR and between f084773 and 98a49ee.

📒 Files selected for processing (3)
  • app/package.json
  • scripts/mock-api-core.mjs
  • src/openhuman/subconscious/schemas.rs
✅ Files skipped from review due to trivial changes (1)
  • app/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/mock-api-core.mjs

…ing crash

- Split rewards-settings.spec.ts into rewards-flow.spec.ts and settings-flow.spec.ts
  so they can be run independently
- Fix Mac2 Settings tab navigation: Home page has a "Settings" button that
  collides with the bottom tab bar; now targets the tab button via
  XCUIElementTypeButton XPath with aria-label filtering
- Consolidate all /settings/* Mac2 navigation into a single handler with
  correct section-first ordering (Account & Security → Billing & Usage)
- Add /rewards to HASH_TO_SIDEBAR_LABEL for Mac2 bottom tab navigation
- Add dismissLocalAISnackbarIfVisible after onboarding in both specs
- Guard .toFixed() calls in InferenceBudget.tsx and PayAsYouGoCard.tsx
  with ?? 0 fallbacks to prevent crash on undefined billing data
- Add missing fields to mock API responses (monthlyBudgetUsd,
  weeklyBudgetUsd, fiveHourCapUsd, cycleEndsAt)
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/test/e2e/helpers/shared-flows.ts (1)

34-53: ⚠️ Potential issue | 🟠 Major

Keep waitForHomePage() limited to home-only markers.

'No messages yet' is rendered by the Conversations empty state, so this helper can now return success while the app is still on /conversations. That makes navigateToHome() and performFullLogin() accept the wrong destination and can hide routing regressions. If you need a broader “authenticated shell is ready” check, split that into a separate helper instead of widening the home-page predicate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/helpers/shared-flows.ts` around lines 34 - 53, The
waitForHomePage function currently treats non-home markers as home (the
candidates array in waitForHomePage includes 'No messages yet' from the
Conversations empty state), causing navigation checks to pass on /conversations;
remove 'No messages yet' from the candidates list in waitForHomePage so it only
matches home-specific markers, and if you need a broader "authenticated shell
ready" check implement a separate helper (e.g., waitForAuthenticatedShell) that
uses the conversations/empty-state markers instead of expanding waitForHomePage.
app/src/components/settings/panels/billing/PayAsYouGoCard.tsx (1)

73-103: ⚠️ Potential issue | 🟠 Major

Normalize topUpBalanceUsd once before using it in the bar math.

Line 77 now guards the text rendering, but Lines 90-100 still read creditBalance.topUpBalanceUsd directly. If that field is undefined, the progress width becomes NaN% and the color classification is wrong, so the UI is still inconsistent for the same payload this change is trying to tolerate. Use one sanitized local value for both the label and the progress calculations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/components/settings/panels/billing/PayAsYouGoCard.tsx` around lines
73 - 103, Normalize topUpBalanceUsd into a single sanitized local constant
(e.g., const topUp = creditBalance.topUpBalanceUsd ?? 0) at the top of the
PayAsYouGoCard rendering scope and replace all direct accesses to
creditBalance.topUpBalanceUsd inside the progress bar className and style (color
classification and width calculation) as well as the text label so calculations
can't produce NaN and the color logic stays consistent when the field is
undefined.
🧹 Nitpick comments (2)
app/test/e2e/specs/settings-flow.spec.ts (1)

29-65: Prefer const + arrow helpers for the new spec-local functions.

stepLog, waitForAnyText, expectAnyText, and navigateToSettingsPanel are introduced as function declarations in a *.ts module. Converting them to const ... = (...) => keeps the file consistent with the repository’s TS convention.

As per coding guidelines: **/*.{js,jsx,ts,tsx}: Use const by default, let if reassignment is needed, avoid var. Prefer arrow functions over function declarations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/settings-flow.spec.ts` around lines 29 - 65, Replace the
four top-level function declarations with const arrow function bindings to match
the project's TypeScript style: change function stepLog(...) { ... } to const
stepLog = (...) => { ... }, waitForAnyText to const waitForAnyText = async (...)
=> { ... }, expectAnyText to const expectAnyText = async (...) => { ... }, and
navigateToSettingsPanel to const navigateToSettingsPanel = async (...) => { ...
}; keep the bodies, signatures, types and exported scope identical and ensure
any references to these identifiers elsewhere remain unchanged.
app/test/e2e/specs/rewards-flow.spec.ts (1)

24-82: Prefer const + arrow helpers for these spec-local utilities.

stepLog, waitForAnyText, expectAnyText, and navigateToRewardsAndWait are all new function declarations in a *.ts module. Converting them to const ... = (...) => keeps the spec aligned with the repo’s TypeScript convention.

As per coding guidelines: **/*.{js,jsx,ts,tsx}: Use const by default, let if reassignment is needed, avoid var. Prefer arrow functions over function declarations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/rewards-flow.spec.ts` around lines 24 - 82, Change the
four top-level function declarations to const arrow helpers to match repo TS
style: replace function stepLog(...) { ... } with const stepLog = (message:
string, context?: unknown) => { ... }, function waitForAnyText(...) with const
waitForAnyText = async (candidates: string[], timeout = 12_000): Promise<string
| null> => { ... }, function expectAnyText(...) with const expectAnyText = async
(candidates: string[], context: string) => { ... }, and function
navigateToRewardsAndWait() with const navigateToRewardsAndWait = async () => {
... }; keep all parameter types, return types, async/await behavior and internal
logic identical, and ensure any references to these symbols remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/test/e2e/specs/rewards-flow.spec.ts`:
- Around line 84-127: Move the login/navigation setup out of the first "it" test
into a beforeEach or shared helper so each spec can run in isolation: perform
triggerAuthDeepLinkBypass + waitForWindowVisible/waitForWebView/waitForAppReady
and the post-login steps completeOnboardingIfVisible,
dismissLocalAISnackbarIfVisible, navigateToRewardsAndWait from a beforeEach (or
a reusable helper called by beforeEach) rather than inside the first test; when
the auth/navigation loop fails, append clearRequestLog()’s saved requests (or
call the request-log reader) alongside dumpAccessibilityTree() in the thrown
error path so the failure diagnostics include both the accessibility tree and
the request log for faster debugging.

In `@app/test/e2e/specs/settings-flow.spec.ts`:
- Around line 83-126: This test embeds login/onboarding/navigation inside the it
block (using triggerAuthDeepLinkBypass, waitForWindowVisible, waitForWebView,
waitForAppReady, completeOnboardingIfVisible, dismissLocalAISnackbarIfVisible,
navigateToSettings) which makes other specs fail when run in isolation; refactor
by moving the auth + onboarding + navigation steps into a beforeEach or a shared
setup helper that each spec calls, ensure waitForAnyText and expectAnyText
checks remain after navigation, and enhance failure diagnostics in that setup
helper so when auth/navigation fails it logs both dumpAccessibilityTree() and
the network/request log (attach request log alongside the accessibility tree) to
aid debugging.

---

Outside diff comments:
In `@app/src/components/settings/panels/billing/PayAsYouGoCard.tsx`:
- Around line 73-103: Normalize topUpBalanceUsd into a single sanitized local
constant (e.g., const topUp = creditBalance.topUpBalanceUsd ?? 0) at the top of
the PayAsYouGoCard rendering scope and replace all direct accesses to
creditBalance.topUpBalanceUsd inside the progress bar className and style (color
classification and width calculation) as well as the text label so calculations
can't produce NaN and the color logic stays consistent when the field is
undefined.

In `@app/test/e2e/helpers/shared-flows.ts`:
- Around line 34-53: The waitForHomePage function currently treats non-home
markers as home (the candidates array in waitForHomePage includes 'No messages
yet' from the Conversations empty state), causing navigation checks to pass on
/conversations; remove 'No messages yet' from the candidates list in
waitForHomePage so it only matches home-specific markers, and if you need a
broader "authenticated shell ready" check implement a separate helper (e.g.,
waitForAuthenticatedShell) that uses the conversations/empty-state markers
instead of expanding waitForHomePage.

---

Nitpick comments:
In `@app/test/e2e/specs/rewards-flow.spec.ts`:
- Around line 24-82: Change the four top-level function declarations to const
arrow helpers to match repo TS style: replace function stepLog(...) { ... } with
const stepLog = (message: string, context?: unknown) => { ... }, function
waitForAnyText(...) with const waitForAnyText = async (candidates: string[],
timeout = 12_000): Promise<string | null> => { ... }, function
expectAnyText(...) with const expectAnyText = async (candidates: string[],
context: string) => { ... }, and function navigateToRewardsAndWait() with const
navigateToRewardsAndWait = async () => { ... }; keep all parameter types, return
types, async/await behavior and internal logic identical, and ensure any
references to these symbols remain unchanged.

In `@app/test/e2e/specs/settings-flow.spec.ts`:
- Around line 29-65: Replace the four top-level function declarations with const
arrow function bindings to match the project's TypeScript style: change function
stepLog(...) { ... } to const stepLog = (...) => { ... }, waitForAnyText to
const waitForAnyText = async (...) => { ... }, expectAnyText to const
expectAnyText = async (...) => { ... }, and navigateToSettingsPanel to const
navigateToSettingsPanel = async (...) => { ... }; keep the bodies, signatures,
types and exported scope identical and ensure any references to these
identifiers elsewhere remain unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f2b6439f-79cd-460e-a139-72dc226aed9b

📥 Commits

Reviewing files that changed from the base of the PR and between 98a49ee and c233dc4.

📒 Files selected for processing (7)
  • app/scripts/e2e-run-all-flows.sh
  • app/src/components/settings/panels/billing/InferenceBudget.tsx
  • app/src/components/settings/panels/billing/PayAsYouGoCard.tsx
  • app/test/e2e/helpers/shared-flows.ts
  • app/test/e2e/specs/rewards-flow.spec.ts
  • app/test/e2e/specs/settings-flow.spec.ts
  • scripts/mock-api-core.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/scripts/e2e-run-all-flows.sh
  • scripts/mock-api-core.mjs

…selectors

E2E spec fixes across all flow specs:

- Replace tautological `res.ok || Boolean(res.error)` assertions with
  explicit success/failure checks and diagnostic logging in discord-flow,
  gmail-flow, notion-flow, macos-distribution, automation-scheduling,
  chat-interface-flow, system-resource-access, and login-flow specs
- Fix channel_web_chat payload shape: use {client_id, thread_id, message}
  instead of wrong {input, channel, target} keys
- Update Skills page selectors for new filter tab UI: use "Channels" tab
  for Telegram/Discord, "Other" tab for Gmail/Notion (replaces old
  "Channel Integrations" / "3rd Party Skills" section headings)
- Fix CTA button text: "Setup"/"Enable"/"Manage" instead of "Configure"
- Fix Notion Enable click: use last matching XPath button (Notion appears
  after Gmail) to avoid clicking Gmail's Enable button
- Fix Mac2 scroll: replace W3C wheel actions (unsupported) with native
  macos:scroll CGEvent in scrollDownInPage
- Add TaskRecurrence::try_cron validated constructor; use it in RPC
  handler and DB deserialization with Pending fallback for corrupt data
- Poll subconscious_log_list with timeout instead of fixed sleep in both
  E2E spec and json_rpc_e2e.rs integration test
- Add missing-param validation test for cron_run (empty job_id)
- Strengthen login-flow macOS fallback: assert isAuthenticated/token
  instead of just RPC reachability
- Accept network errors in macos-distribution app_state_snapshot test
  (no mock server in that spec)
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (2)
app/test/e2e/specs/login-flow.spec.ts (1)

396-402: ⚠️ Potential issue | 🟠 Major

Inconsistent fallback — only checks RPC success, not actual session token.

This macOS fallback only verifies session.ok === true, but per the previous review feedback and the pattern established at lines 175-184, it should also verify that an actual token is present. A logged-out state can also return ok=true.

Suggested fix
     if (!foundText && process.platform === 'darwin') {
       // Appium Mac2 may expose slightly different accessibility labels; treat
       // successful auth/session as equivalent home-shell readiness.
       const session = await callOpenhumanRpc('openhuman.auth_get_session_token', {});
       expect(session.ok).toBe(true);
+      const inner = (session.result as Record<string, unknown>)?.result ?? session.result;
+      const token = (inner as Record<string, unknown>)?.token;
+      console.log('[LoginFlow] macOS fallback: session token present =', !!token);
+      expect(token).toBeTruthy();
       return;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/login-flow.spec.ts` around lines 396 - 402, The macOS
fallback only asserts session.ok but must also verify an actual session token;
update the block that calls callOpenhumanRpc('openhuman.auth_get_session_token',
{}) (variable session) to assert both session.ok === true and that the returned
token is present (e.g., check session.token or session.data?.token consistent
with how tokens are accessed elsewhere), mirroring the token presence check used
in the earlier pattern instead of treating ok=true alone as success.
app/test/e2e/specs/chat-interface-flow.spec.ts (1)

108-134: ⚠️ Potential issue | 🟠 Major

This still doesn't prove channel_web_chat handled the message.

The case passes on any non-validation runtime error, so it can go green even when the handler rejects before creating or enqueuing a chat message. Please assert a concrete side effect from the mock/backend, or rename this to payload-validation coverage only.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/chat-interface-flow.spec.ts` around lines 108 - 134, The
test currently only ensures payload validation and treats any non-validation
runtime error as acceptable, so update the spec to assert a concrete side effect
from the handler: after calling callOpenhumanRpc('openhuman.channel_web_chat',
...) verify the mock/backend received/enqueued the message (e.g., assert the
mock enqueue/socket handler was called or that res.result contains a created
message id/ack field), using the test's existing mock objects or by spying on
the backend enqueue function; if you cannot assert a side effect, rename the
test to indicate it is "payload validation only" and remove the runtime-success
expectation. Ensure you reference callOpenhumanRpc and the
'openhuman.channel_web_chat' handler when adding the spy/assertion.
🧹 Nitpick comments (4)
app/test/e2e/specs/notion-flow.spec.ts (1)

270-275: Unnecessary dynamic import — use static import instead.

Same issue as gmail-flow.spec.ts: scrollToFindText should be added to the existing imports at the top of the file rather than dynamically imported inside tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/notion-flow.spec.ts` around lines 270 - 275, The test
currently uses a dynamic import for scrollToFindText inside the test body;
replace that by adding scrollToFindText to the static import list at the top of
the file (where other helpers are imported) and remove the inline const {
scrollToFindText } = await import('../helpers/element-helpers'); line so the
test uses scrollToFindText directly (update references in notion-flow.spec.ts to
the static symbol).
app/test/e2e/specs/telegram-flow.spec.ts (1)

68-77: Remove unused _waitForRequest function.

This function is defined but never called in the file. The underscore prefix suggests intentional non-use, but it's dead code that should be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/telegram-flow.spec.ts` around lines 68 - 77, Remove the
dead helper function _waitForRequest by deleting its whole definition (async
function _waitForRequest(method: string, urlFragment: string, timeout = 15_000)
{ ... }), and ensure there are no remaining references to it (if any exist,
either remove those calls or replace them with the intended alternative).
app/test/e2e/specs/login-flow.spec.ts (1)

485-488: Weak token validation in bypass auth test.

The assertion JSON.stringify(tokenResult.result || {}).length > 0 only checks that the result serializes to a non-empty string, which could be true for error objects or empty wrappers. Consider checking for actual token presence like the pattern at lines 175-184.

Suggested fix
     // Assert token was persisted at core auth layer
     const tokenResult = await callOpenhumanRpc('openhuman.auth_get_session_token', {});
     expect(tokenResult.ok).toBe(true);
-    expect(JSON.stringify(tokenResult.result || {}).length > 0).toBe(true);
+    const inner = (tokenResult.result as Record<string, unknown>)?.result ?? tokenResult.result;
+    const token = (inner as Record<string, unknown>)?.token;
+    expect(token).toBeTruthy();
     console.log('[LoginFlow] Bypass auth: core session token available');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/login-flow.spec.ts` around lines 485 - 488, Replace the
weak stringify check with a direct assertion that a real session token exists:
after calling callOpenhumanRpc('openhuman.auth_get_session_token') inspect
tokenResult.result (referencing tokenResult and the RPC name) and assert the
expected token field is present and non-empty (e.g., tokenResult.result.token is
a non-empty string and optionally matches a JWT-like pattern with two dots).
Keep the existing expect(tokenResult.ok).toBe(true) and remove the
JSON.stringify length check, logging only after the stronger token assertions
pass.
app/test/e2e/specs/gmail-flow.spec.ts (1)

280-285: Unnecessary dynamic import — scrollToFindText is already available.

scrollToFindText is exported from element-helpers.ts but you're using a dynamic import inside the test. Since the module is already imported at the top of the file, add it to the existing import statement for cleaner code.

Suggested refactor
 import {
   clickText,
   dumpAccessibilityTree,
+  scrollToFindText,
   textExists,
   waitForWebView,
   waitForWindowVisible,
 } from '../helpers/element-helpers';

Then remove the dynamic imports on lines 281, 297, and 311.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/gmail-flow.spec.ts` around lines 280 - 285, Add
scrollToFindText to the existing static import from element-helpers at the top
of the spec and remove the dynamic imports that
import('../helpers/element-helpers') (the places that currently destructure
scrollToFindText) so the test uses the top-level scrollToFindText symbol
directly; update any references that relied on the dynamic import to use the
statically imported scrollToFindText instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/scripts/e2e-run-all-flows.sh`:
- Around line 15-32: The all-flows runner removed the service-connectivity flow
and must re-add its run entry; insert a line invoking the service-connectivity
spec using the same pattern as the others, e.g. run
"test/e2e/specs/service-connectivity-flow.spec.ts" "service-connectivity", so
the OPENHUMAN_SERVICE_MOCK install/start/stop/restart coverage and state-file
handling in e2e-run-spec.sh remain exercised.

In `@app/test/e2e/specs/chat-interface-flow.spec.ts`:
- Around line 293-307: Replace the raw XCUIElementType XPath usage
(textareaSelector and its try/catch block) with a cross-platform helper from
element-helpers.ts: add or reuse a chat-input helper (e.g., clickChatInput or
clickNativeButton('chat-input')) and call that here instead of querying
textareaSelector or using clickText('Type a message'); ensure the spec uses the
new helper to focus the chat input (remove the platform-specific XPath and
related try/catch) and keep the existing logging (stepLog) around the helper
call; follow existing helper patterns like clickNativeButton(), hasAppChrome(),
waitForWebView(), clickToggle() when implementing or picking the helper.

In `@app/test/e2e/specs/notion-flow.spec.ts`:
- Around line 320-343: This spec uses a raw XCUIElementTypeButton XPath and
manual pointer actions; replace that with the element-helpers.ts helpers (e.g.,
clickButton('Enable') or clickNativeButton('Enable')) or add a new Mac2-specific
helper (e.g., findAndClickMacNativeButton) in element-helpers.ts that
encapsulates the XPath+pointer logic, then call that helper from
notion-flow.spec.ts instead of directly using the XPath, getLocation/getSize,
browser.performActions and browser.releaseActions.

In `@app/test/e2e/specs/system-resource-access.spec.ts`:
- Around line 98-105: requireMethod currently only logs and returns false in
non-STRICT mode which lets callers return and mark tests as passed; change it to
mark the test as pending/skipped instead. Update requireMethod(methods, method,
caseId) so that when !methods.has(method) and STRICT is false it calls the test
framework’s pending/skip API (e.g., pending(`${caseId}: RPC method ${method} not
registered`) or test.skip equivalent) and then returns false, so the test runner
records the case as skipped/pending rather than green; keep the STRICT branch
as-is and preserve LOG_PREFIX and caseId in the message.

In `@app/test/e2e/specs/telegram-flow.spec.ts`:
- Around line 324-343: The test is using platform-specific XCUIElementType
selectors instead of the project's element helpers; replace any raw XCUI
XPath/button selectors in this spec with helpers from element-helpers.ts (e.g.,
use clickText('Setup') or clickButton('Setup') for web/DOM labels and
clickNativeButton('Setup') when interacting with native iOS buttons), and prefer
higher-level helpers like clickToggle, hasAppChrome, and waitForWebView where
appropriate so the Telegram card click and subsequent modal assertions use the
cross-platform helpers rather than raw XCUIElementType* XPath selectors.
- Around line 100-106: The current test uses a permissive assertion
"expect(res.ok || Boolean(res.error)).toBe(true)" which treats any RPC response
(including errors) as success; replace it with the pattern used in
gmail-flow.spec.ts: after calling callOpenhumanRpc('openhuman.channels_connect',
...), assert by checking if (!res.ok) { expect(res.error).toBeDefined(); } else
{ expect(res.ok).toBe(true); } to properly distinguish successful responses from
expected failures; apply this same replacement for all similar callOpenhumanRpc
assertions in telegram-flow.spec.ts that check channel connect/interaction
results.

In `@src/openhuman/subconscious/types.rs`:
- Around line 45-54: try_cron currently validates the cron expression using
cron::Schedule::from_str which expects a 6-field (seconds-first) expression, but
TaskRecurrence::Cron is documented as accepting 5-field crons; move the same
normalization used in compute_next_run into try_cron: detect 5-field expressions
and prepend "0 " to produce a 6-field expression before calling
cron::Schedule::from_str, use the normalized string for validation, and store
the normalized cron string in the Cron variant (TaskRecurrence::Cron) on success
so deserialization and validation are consistent with compute_next_run.

---

Duplicate comments:
In `@app/test/e2e/specs/chat-interface-flow.spec.ts`:
- Around line 108-134: The test currently only ensures payload validation and
treats any non-validation runtime error as acceptable, so update the spec to
assert a concrete side effect from the handler: after calling
callOpenhumanRpc('openhuman.channel_web_chat', ...) verify the mock/backend
received/enqueued the message (e.g., assert the mock enqueue/socket handler was
called or that res.result contains a created message id/ack field), using the
test's existing mock objects or by spying on the backend enqueue function; if
you cannot assert a side effect, rename the test to indicate it is "payload
validation only" and remove the runtime-success expectation. Ensure you
reference callOpenhumanRpc and the 'openhuman.channel_web_chat' handler when
adding the spy/assertion.

In `@app/test/e2e/specs/login-flow.spec.ts`:
- Around line 396-402: The macOS fallback only asserts session.ok but must also
verify an actual session token; update the block that calls
callOpenhumanRpc('openhuman.auth_get_session_token', {}) (variable session) to
assert both session.ok === true and that the returned token is present (e.g.,
check session.token or session.data?.token consistent with how tokens are
accessed elsewhere), mirroring the token presence check used in the earlier
pattern instead of treating ok=true alone as success.

---

Nitpick comments:
In `@app/test/e2e/specs/gmail-flow.spec.ts`:
- Around line 280-285: Add scrollToFindText to the existing static import from
element-helpers at the top of the spec and remove the dynamic imports that
import('../helpers/element-helpers') (the places that currently destructure
scrollToFindText) so the test uses the top-level scrollToFindText symbol
directly; update any references that relied on the dynamic import to use the
statically imported scrollToFindText instead.

In `@app/test/e2e/specs/login-flow.spec.ts`:
- Around line 485-488: Replace the weak stringify check with a direct assertion
that a real session token exists: after calling
callOpenhumanRpc('openhuman.auth_get_session_token') inspect tokenResult.result
(referencing tokenResult and the RPC name) and assert the expected token field
is present and non-empty (e.g., tokenResult.result.token is a non-empty string
and optionally matches a JWT-like pattern with two dots). Keep the existing
expect(tokenResult.ok).toBe(true) and remove the JSON.stringify length check,
logging only after the stronger token assertions pass.

In `@app/test/e2e/specs/notion-flow.spec.ts`:
- Around line 270-275: The test currently uses a dynamic import for
scrollToFindText inside the test body; replace that by adding scrollToFindText
to the static import list at the top of the file (where other helpers are
imported) and remove the inline const { scrollToFindText } = await
import('../helpers/element-helpers'); line so the test uses scrollToFindText
directly (update references in notion-flow.spec.ts to the static symbol).

In `@app/test/e2e/specs/telegram-flow.spec.ts`:
- Around line 68-77: Remove the dead helper function _waitForRequest by deleting
its whole definition (async function _waitForRequest(method: string,
urlFragment: string, timeout = 15_000) { ... }), and ensure there are no
remaining references to it (if any exist, either remove those calls or replace
them with the intended alternative).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a21cd80-b369-42f0-a57e-8261c88c872d

📥 Commits

Reviewing files that changed from the base of the PR and between c233dc4 and 507f9ed.

📒 Files selected for processing (15)
  • app/scripts/e2e-run-all-flows.sh
  • app/test/e2e/helpers/element-helpers.ts
  • app/test/e2e/specs/automation-scheduling.spec.ts
  • app/test/e2e/specs/chat-interface-flow.spec.ts
  • app/test/e2e/specs/discord-flow.spec.ts
  • app/test/e2e/specs/gmail-flow.spec.ts
  • app/test/e2e/specs/login-flow.spec.ts
  • app/test/e2e/specs/macos-distribution.spec.ts
  • app/test/e2e/specs/notion-flow.spec.ts
  • app/test/e2e/specs/system-resource-access.spec.ts
  • app/test/e2e/specs/telegram-flow.spec.ts
  • src/openhuman/subconscious/schemas.rs
  • src/openhuman/subconscious/store.rs
  • src/openhuman/subconscious/types.rs
  • tests/json_rpc_e2e.rs
✅ Files skipped from review due to trivial changes (2)
  • app/test/e2e/specs/macos-distribution.spec.ts
  • app/test/e2e/specs/discord-flow.spec.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/json_rpc_e2e.rs
  • src/openhuman/subconscious/schemas.rs
  • app/test/e2e/specs/automation-scheduling.spec.ts

Comment on lines +15 to +32
run "test/e2e/specs/macos-distribution.spec.ts" "macos-distribution"
run "test/e2e/specs/auth-session-management.spec.ts" "auth"
run "test/e2e/specs/permissions-system-access.spec.ts" "permissions-system-access"
run "test/e2e/specs/local-model-runtime.spec.ts" "local-model"
run "test/e2e/specs/system-resource-access.spec.ts" "system-resource-access"
run "test/e2e/specs/memory-system.spec.ts" "memory-system"
run "test/e2e/specs/automation-scheduling.spec.ts" "automation-scheduling"
run "test/e2e/specs/chat-interface-flow.spec.ts" "chat-interface"
run "test/e2e/specs/login-flow.spec.ts" "login"
run "test/e2e/specs/auth-access-control.spec.ts" "auth"
run "test/e2e/specs/telegram-flow.spec.ts" "telegram"
run "test/e2e/specs/discord-flow.spec.ts" "discord"
run "test/e2e/specs/gmail-flow.spec.ts" "gmail"
run "test/e2e/specs/notion-flow.spec.ts" "notion"
run "test/e2e/specs/card-payment-flow.spec.ts" "card-payment"
run "test/e2e/specs/crypto-payment-flow.spec.ts" "crypto-payment"
run "test/e2e/specs/conversations-web-channel-flow.spec.ts" "conversations"
run "test/e2e/specs/local-model-runtime.spec.ts" "local-model"
run "test/e2e/specs/screen-intelligence.spec.ts" "screen-intelligence"
OPENHUMAN_SERVICE_MOCK=1 run "test/e2e/specs/service-connectivity-flow.spec.ts" "service-connectivity"
run "test/e2e/specs/skills-registry.spec.ts" "skills-registry"
run "test/e2e/specs/skill-execution-flow.spec.ts" "skill-execution"
run "test/e2e/specs/navigation.spec.ts" "navigation"
run "test/e2e/specs/smoke.spec.ts" "smoke"
run "test/e2e/specs/tauri-commands.spec.ts" "tauri-commands"
run "test/e2e/specs/voice-mode.spec.ts" "voice-mode"
run "test/e2e/specs/text-autocomplete-flow.spec.ts" "text-autocomplete"
run "test/e2e/specs/rewards-flow.spec.ts" "rewards-flow"
run "test/e2e/specs/settings-flow.spec.ts" "settings-flow"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't drop the service-connectivity flow from the all-flows runner.

app/test/e2e/specs/service-connectivity-flow.spec.ts still owns the OPENHUMAN_SERVICE_MOCK=1 install/start/stop/restart coverage, and app/scripts/e2e-run-spec.sh still has dedicated state-file setup for that mode. Removing it here turns that path into dead coverage for the main E2E entrypoint.

↩️ Suggested addition
 run "test/e2e/specs/notion-flow.spec.ts" "notion"
 run "test/e2e/specs/screen-intelligence.spec.ts" "screen-intelligence"
+OPENHUMAN_SERVICE_MOCK=1 run "test/e2e/specs/service-connectivity-flow.spec.ts" "service-connectivity"
 run "test/e2e/specs/voice-mode.spec.ts" "voice-mode"
 run "test/e2e/specs/text-autocomplete-flow.spec.ts" "text-autocomplete"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
run "test/e2e/specs/macos-distribution.spec.ts" "macos-distribution"
run "test/e2e/specs/auth-session-management.spec.ts" "auth"
run "test/e2e/specs/permissions-system-access.spec.ts" "permissions-system-access"
run "test/e2e/specs/local-model-runtime.spec.ts" "local-model"
run "test/e2e/specs/system-resource-access.spec.ts" "system-resource-access"
run "test/e2e/specs/memory-system.spec.ts" "memory-system"
run "test/e2e/specs/automation-scheduling.spec.ts" "automation-scheduling"
run "test/e2e/specs/chat-interface-flow.spec.ts" "chat-interface"
run "test/e2e/specs/login-flow.spec.ts" "login"
run "test/e2e/specs/auth-access-control.spec.ts" "auth"
run "test/e2e/specs/telegram-flow.spec.ts" "telegram"
run "test/e2e/specs/discord-flow.spec.ts" "discord"
run "test/e2e/specs/gmail-flow.spec.ts" "gmail"
run "test/e2e/specs/notion-flow.spec.ts" "notion"
run "test/e2e/specs/card-payment-flow.spec.ts" "card-payment"
run "test/e2e/specs/crypto-payment-flow.spec.ts" "crypto-payment"
run "test/e2e/specs/conversations-web-channel-flow.spec.ts" "conversations"
run "test/e2e/specs/local-model-runtime.spec.ts" "local-model"
run "test/e2e/specs/screen-intelligence.spec.ts" "screen-intelligence"
OPENHUMAN_SERVICE_MOCK=1 run "test/e2e/specs/service-connectivity-flow.spec.ts" "service-connectivity"
run "test/e2e/specs/skills-registry.spec.ts" "skills-registry"
run "test/e2e/specs/skill-execution-flow.spec.ts" "skill-execution"
run "test/e2e/specs/navigation.spec.ts" "navigation"
run "test/e2e/specs/smoke.spec.ts" "smoke"
run "test/e2e/specs/tauri-commands.spec.ts" "tauri-commands"
run "test/e2e/specs/voice-mode.spec.ts" "voice-mode"
run "test/e2e/specs/text-autocomplete-flow.spec.ts" "text-autocomplete"
run "test/e2e/specs/rewards-flow.spec.ts" "rewards-flow"
run "test/e2e/specs/settings-flow.spec.ts" "settings-flow"
run "test/e2e/specs/macos-distribution.spec.ts" "macos-distribution"
run "test/e2e/specs/auth-session-management.spec.ts" "auth"
run "test/e2e/specs/permissions-system-access.spec.ts" "permissions-system-access"
run "test/e2e/specs/local-model-runtime.spec.ts" "local-model"
run "test/e2e/specs/system-resource-access.spec.ts" "system-resource-access"
run "test/e2e/specs/memory-system.spec.ts" "memory-system"
run "test/e2e/specs/automation-scheduling.spec.ts" "automation-scheduling"
run "test/e2e/specs/chat-interface-flow.spec.ts" "chat-interface"
run "test/e2e/specs/login-flow.spec.ts" "login"
run "test/e2e/specs/telegram-flow.spec.ts" "telegram"
run "test/e2e/specs/discord-flow.spec.ts" "discord"
run "test/e2e/specs/gmail-flow.spec.ts" "gmail"
run "test/e2e/specs/notion-flow.spec.ts" "notion"
run "test/e2e/specs/screen-intelligence.spec.ts" "screen-intelligence"
OPENHUMAN_SERVICE_MOCK=1 run "test/e2e/specs/service-connectivity-flow.spec.ts" "service-connectivity"
run "test/e2e/specs/voice-mode.spec.ts" "voice-mode"
run "test/e2e/specs/text-autocomplete-flow.spec.ts" "text-autocomplete"
run "test/e2e/specs/rewards-flow.spec.ts" "rewards-flow"
run "test/e2e/specs/settings-flow.spec.ts" "settings-flow"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/scripts/e2e-run-all-flows.sh` around lines 15 - 32, The all-flows runner
removed the service-connectivity flow and must re-add its run entry; insert a
line invoking the service-connectivity spec using the same pattern as the
others, e.g. run "test/e2e/specs/service-connectivity-flow.spec.ts"
"service-connectivity", so the OPENHUMAN_SERVICE_MOCK install/start/stop/restart
coverage and state-file handling in e2e-run-spec.sh remain exercised.

Comment on lines +293 to +307
// Find and click the textarea to focus it (Mac2: use accessibility tree)
// The textarea has placeholder "Type a message..." — find it via XPath
const textareaSelector = '//XCUIElementTypeTextArea | //XCUIElementTypeTextField';
let textarea;
try {
textarea = await browser.$(textareaSelector);
if (await textarea.isExisting()) {
await textarea.click();
stepLog('Clicked textarea via accessibility selector');
}
} catch {
stepLog('Could not find textarea via XCUIElementType — trying text match');
try {
await clickText('Type a message', 10_000);
stepLog('Clicked "Type a message" placeholder');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace the raw XCUIElementType* XPath with an E2E helper.

This hard-codes a platform-specific selector and can focus the wrong field if another text input exists. Please add or reuse an element-helpers.ts helper for the chat input instead of querying raw native element types here.

As per coding guidelines, "In E2E test specs, use helpers from element-helpers.ts and never use raw XCUIElementType* selectors. Use clickNativeButton(), hasAppChrome(), waitForWebView(), clickToggle() for cross-platform element interaction."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/chat-interface-flow.spec.ts` around lines 293 - 307,
Replace the raw XCUIElementType XPath usage (textareaSelector and its try/catch
block) with a cross-platform helper from element-helpers.ts: add or reuse a
chat-input helper (e.g., clickChatInput or clickNativeButton('chat-input')) and
call that here instead of querying textareaSelector or using clickText('Type a
message'); ensure the spec uses the new helper to focus the chat input (remove
the platform-specific XPath and related try/catch) and keep the existing logging
(stepLog) around the helper call; follow existing helper patterns like
clickNativeButton(), hasAppChrome(), waitForWebView(), clickToggle() when
implementing or picking the helper.

Comment on lines +320 to +343
const buttons = await browser.$$(
'//XCUIElementTypeButton[contains(@title, "Enable") or contains(@title, "Manage") or contains(@label, "Enable") or contains(@label, "Manage")]'
);
if (buttons.length > 0) {
// Click the last matching button (Notion is after Gmail)
const target = buttons[buttons.length - 1];
const loc = await target.getLocation();
const size = await target.getSize();
const cx = Math.round(loc.x + size.width / 2);
const cy = Math.round(loc.y + size.height / 2);
await browser.performActions([
{
type: 'pointer',
id: 'mouse1',
parameters: { pointerType: 'mouse' },
actions: [
{ type: 'pointerMove', duration: 10, x: cx, y: cy },
{ type: 'pointerDown', button: 0 },
{ type: 'pause', duration: 50 },
{ type: 'pointerUp', button: 0 },
],
},
]);
await browser.releaseActions();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Replace raw XCUIElementType* selector with helper from element-helpers.ts.

This code uses raw XPath with XCUIElementTypeButton, violating the coding guideline. The comment acknowledges this is Mac2-specific logic, but this should be encapsulated in element-helpers.ts rather than inline in the spec.

Consider using clickButton('Enable') or clickNativeButton('Enable') from element-helpers, or extracting this Mac2-specific button-finding logic into a new helper.

As per coding guidelines: "In E2E test specs, use helpers from element-helpers.ts and never use raw XCUIElementType* selectors."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/notion-flow.spec.ts` around lines 320 - 343, This spec
uses a raw XCUIElementTypeButton XPath and manual pointer actions; replace that
with the element-helpers.ts helpers (e.g., clickButton('Enable') or
clickNativeButton('Enable')) or add a new Mac2-specific helper (e.g.,
findAndClickMacNativeButton) in element-helpers.ts that encapsulates the
XPath+pointer logic, then call that helper from notion-flow.spec.ts instead of
directly using the XPath, getLocation/getSize, browser.performActions and
browser.releaseActions.

Comment on lines +98 to +105
function requireMethod(methods: Set<string>, method: string, caseId: string): boolean {
if (methods.has(method)) return true;
if (STRICT) {
expect(methods.has(method)).toBe(true);
return false;
}
console.log(`${LOG_PREFIX} ${caseId}: INCONCLUSIVE — RPC method ${method} not registered`);
return false;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

requireMethod() makes missing RPCs look covered.

In non-strict mode this helper only logs and returns false; every caller then returns from the test body, so the case is reported as passed instead of skipped. Please turn these into real skips/pending tests so missing schema entries don't silently go green.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/system-resource-access.spec.ts` around lines 98 - 105,
requireMethod currently only logs and returns false in non-STRICT mode which
lets callers return and mark tests as passed; change it to mark the test as
pending/skipped instead. Update requireMethod(methods, method, caseId) so that
when !methods.has(method) and STRICT is false it calls the test framework’s
pending/skip API (e.g., pending(`${caseId}: RPC method ${method} not
registered`) or test.skip equivalent) and then returns false, so the test runner
records the case as skipped/pending rather than green; keep the STRICT branch
as-is and preserve LOG_PREFIX and caseId in the message.

Comment on lines +100 to +106
const res = await callOpenhumanRpc('openhuman.channels_connect', {
channel: 'telegram',
authMode: 'managed_dm',
credentials: {},
});
expect(res.ok || Boolean(res.error)).toBe(true);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Permissive RPC assertion accepts arbitrary failures as success.

expect(res.ok || Boolean(res.error)).toBe(true) passes whenever the RPC returns any error, including validation failures, transport errors, or malformed responses. This makes the test a "method responded somehow" check rather than verifying actual Telegram channel behavior.

The gmail-flow.spec.ts was updated to use if (!res.ok) { expect(res.error).toBeDefined(); } which properly distinguishes expected failures. Apply the same pattern here for consistency.

Suggested fix
     const res = await callOpenhumanRpc('openhuman.channels_connect', {
       channel: 'telegram',
       authMode: 'managed_dm',
       credentials: {},
     });
-    expect(res.ok || Boolean(res.error)).toBe(true);
+    if (!res.ok) {
+      stepLog(`8.1.1 channels_connect failed (expected without credentials): ${res.error}`);
+      expect(res.error).toBeDefined();
+    }

Apply similarly to lines 121-122, 135-136, 158, 178, 195, and 200.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const res = await callOpenhumanRpc('openhuman.channels_connect', {
channel: 'telegram',
authMode: 'managed_dm',
credentials: {},
});
expect(res.ok || Boolean(res.error)).toBe(true);
});
const res = await callOpenhumanRpc('openhuman.channels_connect', {
channel: 'telegram',
authMode: 'managed_dm',
credentials: {},
});
if (!res.ok) {
stepLog(`8.1.1 channels_connect failed (expected without credentials): ${res.error}`);
expect(res.error).toBeDefined();
}
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/telegram-flow.spec.ts` around lines 100 - 106, The current
test uses a permissive assertion "expect(res.ok ||
Boolean(res.error)).toBe(true)" which treats any RPC response (including errors)
as success; replace it with the pattern used in gmail-flow.spec.ts: after
calling callOpenhumanRpc('openhuman.channels_connect', ...), assert by checking
if (!res.ok) { expect(res.error).toBeDefined(); } else {
expect(res.ok).toBe(true); } to properly distinguish successful responses from
expected failures; apply this same replacement for all similar callOpenhumanRpc
assertions in telegram-flow.spec.ts that check channel connect/interaction
results.

Comment on lines +324 to +343
it('8.5.3 — Click Setup opens ChannelSetupModal with auth modes, buttons, and fields', async () => {
// Click the Telegram card CTA ("Setup" or "Manage") to open the modal.
stepLog('clicking Telegram card to open Setup modal');

await closeModalIfOpen();
await navigateToHome();
console.log(`${LOG_PREFIX} 7.5.3 PASSED`);
return;
// Try clicking card CTA first, then fallback to card name
try {
await clickText('Setup', 10_000);
} catch {
try {
await clickText('Telegram', 10_000);
} catch {
await clickText('Manage', 10_000);
}
}
await browser.pause(3_000);

// Management panel is open — look for "Re-run Setup" button
expect(modalState).toBe('manage');
// Dump tree to see what the modal looks like in Mac2 accessibility tree
const tree = await dumpAccessibilityTree();
stepLog('Tree after clicking Telegram card:', tree.slice(0, 5000));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Replace raw XCUIElementType* selector with helper from element-helpers.ts.

This code uses raw XPath with XCUIElementTypeButton, which violates the coding guideline: "In E2E test specs, use helpers from element-helpers.ts and never use raw XCUIElementType* selectors."

Consider using clickText('Setup') or clickButton('Setup') from element-helpers instead, which handle cross-platform differences internally.

As per coding guidelines: "In E2E test specs, use helpers from element-helpers.ts and never use raw XCUIElementType* selectors. Use clickNativeButton(), hasAppChrome(), waitForWebView(), clickToggle() for cross-platform element interaction."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/telegram-flow.spec.ts` around lines 324 - 343, The test is
using platform-specific XCUIElementType selectors instead of the project's
element helpers; replace any raw XCUI XPath/button selectors in this spec with
helpers from element-helpers.ts (e.g., use clickText('Setup') or
clickButton('Setup') for web/DOM labels and clickNativeButton('Setup') when
interacting with native iOS buttons), and prefer higher-level helpers like
clickToggle, hasAppChrome, and waitForWebView where appropriate so the Telegram
card click and subsequent modal assertions use the cross-platform helpers rather
than raw XCUIElementType* XPath selectors.

- Add skillId prop to UnifiedSkillCard, rendered as data-testid and
  aria-label on the CTA button for unique identification
- Add clickByTestId helper: uses CSS [data-testid] on tauri-driver,
  index-based "Settings" button matching on Mac2 (WKWebView doesn't
  expose data-testid or aria-label in accessibility tree)
- Update screen-intelligence, voice-mode, text-autocomplete specs to
  click the correct built-in card's Settings button via clickByTestId
- Fix category text assertions: "Built-in Skills" → accept "Built-in",
  "Channel Integrations" → accept "Channels", "3rd Party" → accept "Other"
…types

- Update CI test workflow with improved E2E matrix and caching
- Harden e2e-build/run scripts with better platform detection and cleanup
- Fix rewards-flow and settings-flow specs for Mac2 navigation
- Update local-model-runtime spec with additional assertions
- Update wdio.conf and platform helpers for cross-platform reliability
- Update subconscious domain: engine, store, schemas, types, integration test
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
app/test/e2e/helpers/platform.ts (1)

10-24: Documentation outdated after adding Windows support.

The docstring at line 10 says tauri-driver (Linux) but the fallback now returns true for both linux and win32. Update the comment to reflect Windows support.

- *  - **tauri-driver** (Linux): WebDriver server shipped by the Tauri project.
+ *  - **tauri-driver** (Linux/Windows): WebDriver server shipped by the Tauri project.

Similarly, the function docstring at line 16 says "(Linux E2E)" but should now say "(Linux/Windows E2E)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/helpers/platform.ts` around lines 10 - 24, Update the outdated
comments to reflect Windows support: change the top docstring that currently
mentions "tauri-driver (Linux)" to something like "tauri-driver (Linux/Windows)"
and update the isTauriDriver() function comment that reads "(Linux E2E)" to
"(Linux/Windows E2E)"; ensure both references to platform checks mention Linux
and Windows consistently so the documentation matches the fallback logic in
isTauriDriver().
.github/workflows/test.yml (2)

359-374: Use find instead of ls for safer filename handling (SC2012).

The ls commands in sidecar staging can fail with non-alphanumeric filenames. Consider using find:

-          SIDECAR_SRC="$(ls app/src-tauri/binaries/openhuman-core-*.exe 2>/dev/null | head -1)"
+          SIDECAR_SRC="$(find app/src-tauri/binaries -maxdepth 1 -name 'openhuman-core-*.exe' -print -quit 2>/dev/null)"
           if [ -z "$SIDECAR_SRC" ]; then
             echo "WARNING: No Windows sidecar found in binaries/, attempting to stage from target/"
-            SIDECAR_SRC="$(ls target/debug/openhuman-core.exe 2>/dev/null | head -1)"
+            SIDECAR_SRC="$(find target/debug -maxdepth 1 -name 'openhuman-core.exe' -print -quit 2>/dev/null)"
           fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/test.yml around lines 359 - 374, Replace unsafe ls usage
when locating the sidecar with find to handle arbitrary filenames: set
SIDECAR_SRC by using find to search for
"app/src-tauri/binaries/openhuman-core-*.exe" (and fallback to
"target/debug/openhuman-core.exe") with -maxdepth 1 and -print -quit to get the
first match, then proceed to cp to
app/src-tauri/target/debug/openhuman-core-x86_64-pc-windows-msvc.exe and the
existing ls display; update the shell block that sets SIDECAR_SRC and the
fallback logic so all filename discovery uses find instead of ls.

280-296: Quote the spec variable to prevent word splitting (SC2086).

The $spec variable in the for loop should be double-quoted to handle paths with spaces correctly.

-            bash scripts/e2e-run-spec.sh "$spec" "$SPEC_NAME" || {
+            bash scripts/e2e-run-spec.sh "$spec" "$SPEC_NAME" || {

Actually, the $spec is already quoted on line 291. The shellcheck warning at line 281 refers to line 12 within the inline script, which corresponds to the for spec in ... loop variable expansion. The loop values are hardcoded paths without spaces, so this is a low-risk warning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/test.yml around lines 280 - 296, The for-loop in the Run
E2E core specs inline script risks word-splitting; update the loop that declares
spec to quote each path (e.g. for spec in "test/e2e/specs/login-flow.spec.ts"
"test/e2e/specs/smoke.spec.ts" ...; do) or convert the list to a quoted array
and iterate over it, ensuring $spec is always double-quoted when expanded (used
by SPEC_NAME=$(basename "$spec" .spec.ts) and when passed to
scripts/e2e-run-spec.sh) so paths with spaces are handled safely.
app/test/e2e/helpers/element-helpers.ts (1)

346-351: Consider documenting the coupling with Skills.tsx skill order.

BUILTIN_SKILL_ORDER must match the render order of BUILT_IN_SKILLS in app/src/pages/Skills.tsx (lines 63-116). Currently both are in sync (screen-intelligence, text-autocomplete, voice-stt), but changes to the UI order will silently break Mac2 E2E tests.

Consider adding a comment with the source file reference, or extracting the order to a shared constant importable by both.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/helpers/element-helpers.ts` around lines 346 - 351,
BUILTIN_SKILL_ORDER in element-helpers.ts is tightly coupled to the render order
of BUILT_IN_SKILLS in Skills.tsx; either add a clear comment above
BUILTIN_SKILL_ORDER mentioning the exact source (Skills.tsx lines where
BUILT_IN_SKILLS is defined) and a reminder to update it when the UI order
changes, or refactor by extracting the order into a single exported constant
(e.g., export const BUILT_IN_SKILL_IDS) in Skills.tsx or a new shared module and
import that constant into element-helpers.ts so both use the same authoritative
list (update any references to BUILTIN_SKILL_ORDER to use the imported
constant).
app/test/e2e/specs/text-autocomplete-flow.spec.ts (1)

1-1: Consider aligning TypeScript directive usage with project pattern.

The @ts-nocheck is consistent with all other E2E specs in the project (23 files). If using targeted @ts-expect-error comments is the preferred approach, apply it project-wide rather than to this file alone. Otherwise, document why E2E tests use this pattern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/text-autocomplete-flow.spec.ts` at line 1, The file
text-autocomplete-flow.spec.ts currently uses the global TypeScript directive
"// `@ts-nocheck`" which is inconsistent with the suggested targeted approach;
either remove this directive and replace it with precise "// `@ts-expect-error`"
comments only where type errors occur inside this spec (identify offending lines
while running the spec and add ts-expect-error immediately above those lines),
or keep the ts-nocheck but update project documentation explaining why E2E specs
use this pattern; update the file to follow whichever project-wide convention
you choose so this spec matches the other 23 E2E specs or the documented
decision.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/test/e2e/helpers/element-helpers.ts`:
- Around line 346-394: The Mac2 branch of clickByTestId can produce index = -1
when testId doesn't match /^skill-cta-{skillId}/ or skillId isn't in
BUILTIN_SKILL_ORDER, which leads to accessing buttons[-1]; validate the
extracted skillId and index before the polling loop: parse skillId from testId
(e.g. with /^skill-cta-(.+)$/), check that BUILTIN_SKILL_ORDER.indexOf(skillId)
returns >= 0, and if not immediately throw a clear Error mentioning
clickByTestId, the invalid testId/skillId and the expected BUILTIN_SKILL_ORDER
values so the failure is deterministic and not a confusing runtime error.

In `@app/test/e2e/specs/voice-mode.spec.ts`:
- Around line 177-183: The test is logging a non-existent field
`whisper_in_process`; update the `stepLog` call in voice-mode.spec.ts to stop
referencing `data.whisper_in_process` (either remove that property or replace it
with a real indicator like `Boolean(data.whisper_binary)`), and if unsure,
inspect the RPC response shaped by the VoiceStatus struct
(src/openhuman/voice/types.rs) to use only actual fields present in the
response.

---

Nitpick comments:
In @.github/workflows/test.yml:
- Around line 359-374: Replace unsafe ls usage when locating the sidecar with
find to handle arbitrary filenames: set SIDECAR_SRC by using find to search for
"app/src-tauri/binaries/openhuman-core-*.exe" (and fallback to
"target/debug/openhuman-core.exe") with -maxdepth 1 and -print -quit to get the
first match, then proceed to cp to
app/src-tauri/target/debug/openhuman-core-x86_64-pc-windows-msvc.exe and the
existing ls display; update the shell block that sets SIDECAR_SRC and the
fallback logic so all filename discovery uses find instead of ls.
- Around line 280-296: The for-loop in the Run E2E core specs inline script
risks word-splitting; update the loop that declares spec to quote each path
(e.g. for spec in "test/e2e/specs/login-flow.spec.ts"
"test/e2e/specs/smoke.spec.ts" ...; do) or convert the list to a quoted array
and iterate over it, ensuring $spec is always double-quoted when expanded (used
by SPEC_NAME=$(basename "$spec" .spec.ts) and when passed to
scripts/e2e-run-spec.sh) so paths with spaces are handled safely.

In `@app/test/e2e/helpers/element-helpers.ts`:
- Around line 346-351: BUILTIN_SKILL_ORDER in element-helpers.ts is tightly
coupled to the render order of BUILT_IN_SKILLS in Skills.tsx; either add a clear
comment above BUILTIN_SKILL_ORDER mentioning the exact source (Skills.tsx lines
where BUILT_IN_SKILLS is defined) and a reminder to update it when the UI order
changes, or refactor by extracting the order into a single exported constant
(e.g., export const BUILT_IN_SKILL_IDS) in Skills.tsx or a new shared module and
import that constant into element-helpers.ts so both use the same authoritative
list (update any references to BUILTIN_SKILL_ORDER to use the imported
constant).

In `@app/test/e2e/helpers/platform.ts`:
- Around line 10-24: Update the outdated comments to reflect Windows support:
change the top docstring that currently mentions "tauri-driver (Linux)" to
something like "tauri-driver (Linux/Windows)" and update the isTauriDriver()
function comment that reads "(Linux E2E)" to "(Linux/Windows E2E)"; ensure both
references to platform checks mention Linux and Windows consistently so the
documentation matches the fallback logic in isTauriDriver().

In `@app/test/e2e/specs/text-autocomplete-flow.spec.ts`:
- Line 1: The file text-autocomplete-flow.spec.ts currently uses the global
TypeScript directive "// `@ts-nocheck`" which is inconsistent with the suggested
targeted approach; either remove this directive and replace it with precise "//
`@ts-expect-error`" comments only where type errors occur inside this spec
(identify offending lines while running the spec and add ts-expect-error
immediately above those lines), or keep the ts-nocheck but update project
documentation explaining why E2E specs use this pattern; update the file to
follow whichever project-wide convention you choose so this spec matches the
other 23 E2E specs or the documented decision.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2cd07993-dd14-493d-9187-d6148c7f2790

📥 Commits

Reviewing files that changed from the base of the PR and between 507f9ed and 3a7a443.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (20)
  • .github/workflows/test.yml
  • app/scripts/e2e-build.sh
  • app/scripts/e2e-run-all-flows.sh
  • app/scripts/e2e-run-spec.sh
  • app/src/components/skills/SkillCard.tsx
  • app/src/pages/Skills.tsx
  • app/test/e2e/helpers/element-helpers.ts
  • app/test/e2e/helpers/platform.ts
  • app/test/e2e/specs/local-model-runtime.spec.ts
  • app/test/e2e/specs/rewards-flow.spec.ts
  • app/test/e2e/specs/screen-intelligence.spec.ts
  • app/test/e2e/specs/settings-flow.spec.ts
  • app/test/e2e/specs/text-autocomplete-flow.spec.ts
  • app/test/e2e/specs/voice-mode.spec.ts
  • app/test/wdio.conf.ts
  • src/openhuman/subconscious/engine.rs
  • src/openhuman/subconscious/integration_test.rs
  • src/openhuman/subconscious/schemas.rs
  • src/openhuman/subconscious/store.rs
  • src/openhuman/subconscious/types.rs
✅ Files skipped from review due to trivial changes (1)
  • src/openhuman/subconscious/schemas.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • app/scripts/e2e-run-spec.sh
  • src/openhuman/subconscious/types.rs
  • app/test/e2e/specs/screen-intelligence.spec.ts
  • app/test/e2e/specs/rewards-flow.spec.ts
  • app/test/e2e/specs/settings-flow.spec.ts

Comment on lines +346 to +394
/**
* Built-in skill card order on the Skills page. The BUILT_IN_SKILLS array
* in Skills.tsx renders cards in this fixed order, so the Nth "Settings"
* button inside the Built-in group corresponds to the Nth skill here.
*/
const BUILTIN_SKILL_ORDER = ['screen-intelligence', 'text-autocomplete', 'voice-stt'];

/**
* Wait for a built-in skill's CTA button and click it.
*
* - tauri-driver: CSS `[data-testid="skill-cta-{skillId}"]`
* - Mac2: WKWebView doesn't expose data-testid or aria-label in its
* accessibility tree. Instead we find all visible "Settings" buttons
* and click the one at the correct index (cards render in fixed order).
*/
export async function clickByTestId(
testId: string,
timeout: number = 15_000
): Promise<ChainablePromiseElement> {
if (isTauriDriver()) {
const el = await browser.$(`[data-testid="${testId}"]`);
await el.waitForExist({
timeout,
timeoutMsg: `Element [data-testid="${testId}"] not found within ${timeout}ms`,
});
await clickAtElement(el);
return el;
}

// Mac2 path: find the Nth "Settings" button by card order.
const skillId = testId.replace(/^skill-cta-/, '');
const index = BUILTIN_SKILL_ORDER.indexOf(skillId);

const literal = xpathStringLiteral('Settings');
const xpath =
`//XCUIElementTypeButton[contains(@label, ${literal}) or ` + `contains(@title, ${literal})]`;

const deadline = Date.now() + timeout;
while (Date.now() < deadline) {
const buttons = await browser.$$(xpath);
if (buttons.length > index) {
await clickAtElement(buttons[index]);
return buttons[index];
}
await browser.pause(500);
}
throw new Error(
`Built-in skill CTA "${testId}" (index ${index}) not found within ${timeout}ms — fewer than ${index + 1} "Settings" buttons visible`
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle unknown testId to prevent silent failures on Mac2.

When testId doesn't match the skill-cta-{skillId} pattern or skillId isn't in BUILTIN_SKILL_ORDER, indexOf returns -1. This causes buttons.length > -1 to always be true, and buttons[-1] returns undefined in JavaScript (not the last element), causing clickAtElement(undefined) to fail with a confusing error.

🐛 Proposed fix to validate index
   // Mac2 path: find the Nth "Settings" button by card order.
   const skillId = testId.replace(/^skill-cta-/, '');
   const index = BUILTIN_SKILL_ORDER.indexOf(skillId);
+
+  if (index === -1) {
+    throw new Error(
+      `clickByTestId: Unknown skill "${skillId}" — not in BUILTIN_SKILL_ORDER. ` +
+      `Mac2 cannot locate elements by data-testid; only built-in skills are supported.`
+    );
+  }

   const literal = xpathStringLiteral('Settings');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Built-in skill card order on the Skills page. The BUILT_IN_SKILLS array
* in Skills.tsx renders cards in this fixed order, so the Nth "Settings"
* button inside the Built-in group corresponds to the Nth skill here.
*/
const BUILTIN_SKILL_ORDER = ['screen-intelligence', 'text-autocomplete', 'voice-stt'];
/**
* Wait for a built-in skill's CTA button and click it.
*
* - tauri-driver: CSS `[data-testid="skill-cta-{skillId}"]`
* - Mac2: WKWebView doesn't expose data-testid or aria-label in its
* accessibility tree. Instead we find all visible "Settings" buttons
* and click the one at the correct index (cards render in fixed order).
*/
export async function clickByTestId(
testId: string,
timeout: number = 15_000
): Promise<ChainablePromiseElement> {
if (isTauriDriver()) {
const el = await browser.$(`[data-testid="${testId}"]`);
await el.waitForExist({
timeout,
timeoutMsg: `Element [data-testid="${testId}"] not found within ${timeout}ms`,
});
await clickAtElement(el);
return el;
}
// Mac2 path: find the Nth "Settings" button by card order.
const skillId = testId.replace(/^skill-cta-/, '');
const index = BUILTIN_SKILL_ORDER.indexOf(skillId);
const literal = xpathStringLiteral('Settings');
const xpath =
`//XCUIElementTypeButton[contains(@label, ${literal}) or ` + `contains(@title, ${literal})]`;
const deadline = Date.now() + timeout;
while (Date.now() < deadline) {
const buttons = await browser.$$(xpath);
if (buttons.length > index) {
await clickAtElement(buttons[index]);
return buttons[index];
}
await browser.pause(500);
}
throw new Error(
`Built-in skill CTA "${testId}" (index ${index}) not found within ${timeout}ms — fewer than ${index + 1} "Settings" buttons visible`
);
// Mac2 path: find the Nth "Settings" button by card order.
const skillId = testId.replace(/^skill-cta-/, '');
const index = BUILTIN_SKILL_ORDER.indexOf(skillId);
if (index === -1) {
throw new Error(
`clickByTestId: Unknown skill "${skillId}" — not in BUILTIN_SKILL_ORDER. ` +
`Mac2 cannot locate elements by data-testid; only built-in skills are supported.`
);
}
const literal = xpathStringLiteral('Settings');
const xpath =
`//XCUIElementTypeButton[contains(`@label`, ${literal}) or ` + `contains(`@title`, ${literal})]`;
const deadline = Date.now() + timeout;
while (Date.now() < deadline) {
const buttons = await browser.$$(xpath);
if (buttons.length > index) {
await clickAtElement(buttons[index]);
return buttons[index];
}
await browser.pause(500);
}
throw new Error(
`Built-in skill CTA "${testId}" (index ${index}) not found within ${timeout}ms — fewer than ${index + 1} "Settings" buttons visible`
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/helpers/element-helpers.ts` around lines 346 - 394, The Mac2
branch of clickByTestId can produce index = -1 when testId doesn't match
/^skill-cta-{skillId}/ or skillId isn't in BUILTIN_SKILL_ORDER, which leads to
accessing buttons[-1]; validate the extracted skillId and index before the
polling loop: parse skillId from testId (e.g. with /^skill-cta-(.+)$/), check
that BUILTIN_SKILL_ORDER.indexOf(skillId) returns >= 0, and if not immediately
throw a clear Error mentioning clickByTestId, the invalid testId/skillId and the
expected BUILTIN_SKILL_ORDER values so the failure is deterministic and not a
confusing runtime error.

Comment on lines +177 to +183
stepLog('Voice availability', {
stt_available: data.stt_available,
tts_available: data.tts_available,
whisper_binary: data.whisper_binary,
piper_binary: data.piper_binary,
whisper_in_process: data.whisper_in_process,
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Logging a field that may not exist in the response.

The log statement references data.whisper_in_process, but the VoiceStatus struct (from src/openhuman/voice/types.rs) doesn't include this field. The struct contains whisper_binary (an Option<String>) but not a boolean whisper_in_process.

This won't break the test but will log undefined. Either remove it or verify the actual RPC response shape.

     stepLog('Voice availability', {
       stt_available: data.stt_available,
       tts_available: data.tts_available,
       whisper_binary: data.whisper_binary,
       piper_binary: data.piper_binary,
-      whisper_in_process: data.whisper_in_process,
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
stepLog('Voice availability', {
stt_available: data.stt_available,
tts_available: data.tts_available,
whisper_binary: data.whisper_binary,
piper_binary: data.piper_binary,
whisper_in_process: data.whisper_in_process,
});
stepLog('Voice availability', {
stt_available: data.stt_available,
tts_available: data.tts_available,
whisper_binary: data.whisper_binary,
piper_binary: data.piper_binary,
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/test/e2e/specs/voice-mode.spec.ts` around lines 177 - 183, The test is
logging a non-existent field `whisper_in_process`; update the `stepLog` call in
voice-mode.spec.ts to stop referencing `data.whisper_in_process` (either remove
that property or replace it with a real indicator like
`Boolean(data.whisper_binary)`), and if unsure, inspect the RPC response shaped
by the VoiceStatus struct (src/openhuman/voice/types.rs) to use only actual
fields present in the response.

- Replace hardcoded 4-spec list in Linux/macOS/Windows CI jobs with
  e2e-run-all-flows.sh (single source of truth for spec list)
- Enable macOS (Appium) and Windows (tauri-driver) E2E on PRs and pushes
- Fix core-rpc-webview.ts: use window.__TAURI__.core.invoke instead of
  dynamic import('@tauri-apps/api/core') which fails on tauri-driver
# Conflicts:
#	app/src/components/settings/panels/billing/InferenceBudget.tsx
#	app/src/components/settings/panels/billing/PayAsYouGoCard.tsx
Update rewards/discord/gmail/notion/settings/system-resource/voice specs to
use scroll-to-find helpers so assertions work even when target content sits
below the WebView fold. Align text expectations with actual page content
where backend fixtures aren't provided.
- e2e-run-all-flows.sh: drop `set -e`, run every spec, collect failures,
  print a summary, and exit non-zero only if any spec actually failed.
  Previously the first failing spec aborted the entire sequential run,
  so specs after macos-distribution never executed in CI.

- macos-distribution.spec.ts: make the `before all` RPC probe soft —
  catch the "Core JSON-RPC not reachable" error instead of throwing
  from the hook. RPC-dependent cases now auto-skip via a new
  runRpcMacOnlyCase helper, while the filesystem-only distribution
  checks (bundle layout, Info.plist, binary presence) continue to run.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants